Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 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 | |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 17 | #include "androidfw/AttributeResolution.h" |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 18 | |
| 19 | #include <cstdint> |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 20 | |
Mark Salyzyn | 6f773a0 | 2016-09-28 16:15:30 -0700 | [diff] [blame] | 21 | #include <log/log.h> |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 22 | |
| 23 | #include "androidfw/AttributeFinder.h" |
| 24 | #include "androidfw/ResourceTypes.h" |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 25 | |
| 26 | constexpr bool kDebugStyles = false; |
| 27 | |
| 28 | namespace android { |
| 29 | |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 30 | class XmlAttributeFinder |
| 31 | : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 32 | public: |
| 33 | explicit XmlAttributeFinder(const ResXMLParser* parser) |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 34 | : BackTrackingAttributeFinder( |
| 35 | 0, parser != nullptr ? parser->getAttributeCount() : 0), |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 36 | parser_(parser) {} |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 37 | |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 38 | inline uint32_t GetAttribute(size_t index) const { |
| 39 | return parser_->getAttributeNameResID(index); |
| 40 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 41 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 42 | private: |
| 43 | const ResXMLParser* parser_; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 44 | }; |
| 45 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 46 | class BagAttributeFinder |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 47 | : public BackTrackingAttributeFinder<BagAttributeFinder, |
| 48 | const ResTable::bag_entry*> { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 49 | public: |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 50 | BagAttributeFinder(const ResTable::bag_entry* start, |
| 51 | const ResTable::bag_entry* end) |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 52 | : BackTrackingAttributeFinder(start, end) {} |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 53 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 54 | inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { |
| 55 | return entry->map.name.ident; |
| 56 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 57 | }; |
| 58 | |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 59 | bool 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) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 64 | if (kDebugStyles) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 65 | ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, |
| 66 | def_style_attr, def_style_res); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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; |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 79 | if (theme->getAttribute(def_style_attr, &value, |
| 80 | &def_style_bag_type_set_flags) >= 0) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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. |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 91 | const ResTable::bag_entry* def_style_start = nullptr; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 92 | uint32_t def_style_type_set_flags = 0; |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 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; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 97 | def_style_type_set_flags |= def_style_bag_type_set_flags; |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 98 | const ResTable::bag_entry* const def_style_end = |
| 99 | def_style_start + (bag_off >= 0 ? bag_off : 0); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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 | |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 107 | if (kDebugStyles) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 108 | ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 109 | } |
| 110 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 111 | ssize_t block = -1; |
| 112 | uint32_t type_set_flags = 0; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 113 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 114 | value.dataType = Res_value::TYPE_NULL; |
| 115 | value.data = Res_value::DATA_NULL_UNDEFINED; |
| 116 | config.density = 0; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 117 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 127 | ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, |
| 128 | value.data); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 129 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 130 | } |
| 131 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 132 | if (value.dataType == Res_value::TYPE_NULL) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 133 | const ResTable::bag_entry* const def_style_entry = |
| 134 | def_style_attr_finder.Find(cur_ident); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 139 | if (kDebugStyles) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 140 | ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, |
| 141 | value.data); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 142 | } |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 143 | } |
| 144 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 145 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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. |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 149 | ssize_t new_block = theme->resolveAttributeReference( |
| 150 | &value, block, &resid, &type_set_flags, &config); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 151 | if (new_block >= 0) block = new_block; |
| 152 | if (kDebugStyles) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 153 | ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, |
| 154 | value.data); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 155 | } |
| 156 | } else { |
| 157 | // If we still don't have a value for this attribute, try to find |
| 158 | // it in the theme! |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 159 | ssize_t new_block = |
| 160 | theme->getAttribute(cur_ident, &value, &type_set_flags); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 161 | if (new_block >= 0) { |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 162 | if (kDebugStyles) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 163 | ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, |
| 164 | value.data); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 165 | } |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 166 | new_block = res.resolveReference(&value, new_block, &resid, |
| 167 | &type_set_flags, &config); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 168 | if (new_block >= 0) block = new_block; |
| 169 | if (kDebugStyles) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 170 | ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, |
| 171 | value.data); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 172 | } |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 173 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 174 | } |
| 175 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 184 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 185 | |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 186 | if (kDebugStyles) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 187 | ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, |
| 188 | value.dataType, value.data); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 189 | } |
| 190 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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] = |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 195 | block != -1 ? static_cast<uint32_t>(res.getTableCookie(block)) |
| 196 | : static_cast<uint32_t>(-1); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 197 | out_values[STYLE_RESOURCE_ID] = resid; |
| 198 | out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; |
| 199 | out_values[STYLE_DENSITY] = config.density; |
| 200 | |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 201 | if (out_indices != nullptr && value.dataType != Res_value::TYPE_NULL) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 202 | indices_idx++; |
| 203 | out_indices[indices_idx] = ii; |
| 204 | } |
| 205 | |
| 206 | out_values += STYLE_NUM_ENTRIES; |
| 207 | } |
| 208 | |
| 209 | res.unlock(); |
| 210 | |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 211 | if (out_indices != nullptr) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 212 | out_indices[0] = indices_idx; |
| 213 | } |
| 214 | return true; |
| 215 | } |
| 216 | |
John Reck | f32adf4 | 2016-11-23 10:39:40 -0800 | [diff] [blame] | 217 | void 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) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 220 | if (kDebugStyles) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 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); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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) { |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 234 | Res_value value; |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 235 | if (theme->getAttribute(def_style_attr, &value, |
| 236 | &def_style_bag_type_set_flags) >= 0) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 237 | if (value.dataType == Res_value::TYPE_REFERENCE) { |
| 238 | def_style_res = value.data; |
| 239 | } |
| 240 | } |
| 241 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 242 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 243 | // Retrieve the style class associated with the current XML tag. |
| 244 | int style = 0; |
| 245 | uint32_t style_bag_type_set_flags = 0; |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 246 | if (xml_parser != nullptr) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 247 | ssize_t idx = xml_parser->indexOfStyle(); |
| 248 | if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { |
| 249 | if (value.dataType == value.TYPE_ATTRIBUTE) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 250 | if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < |
| 251 | 0) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 252 | value.dataType = Res_value::TYPE_NULL; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 253 | } |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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. |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 265 | const ResTable::bag_entry* def_style_attr_start = nullptr; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 266 | uint32_t def_style_type_set_flags = 0; |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 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; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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); |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 274 | BagAttributeFinder def_style_attr_finder(def_style_attr_start, |
| 275 | def_style_attr_end); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 276 | |
| 277 | // Retrieve the style class bag, if requested. |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 278 | const ResTable::bag_entry* style_attr_start = nullptr; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 279 | uint32_t style_type_set_flags = 0; |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 280 | bag_off = |
| 281 | style != 0 |
| 282 | ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) |
| 283 | : -1; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 284 | style_type_set_flags |= style_bag_type_set_flags; |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 285 | const ResTable::bag_entry* const style_attr_end = |
| 286 | style_attr_start + (bag_off >= 0 ? bag_off : 0); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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); |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 292 | const size_t xml_attr_end = |
| 293 | xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 302 | } |
| 303 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 321 | ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, |
| 322 | value.data); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 323 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 324 | } |
| 325 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 326 | if (value.dataType == Res_value::TYPE_NULL) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 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); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 336 | if (kDebugStyles) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 337 | ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, |
| 338 | value.data); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 339 | } |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 340 | } |
| 341 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 342 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 343 | if (value.dataType == Res_value::TYPE_NULL) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 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); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 354 | ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, |
| 355 | value.data); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 356 | } |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 357 | } |
| 358 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 359 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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. |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 363 | ssize_t new_block = theme->resolveAttributeReference( |
| 364 | &value, block, &resid, &type_set_flags, &config); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 365 | if (new_block >= 0) { |
| 366 | block = new_block; |
| 367 | } |
| 368 | |
| 369 | if (kDebugStyles) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 370 | ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, |
| 371 | value.data); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 372 | } |
| 373 | } else { |
| 374 | // If we still don't have a value for this attribute, try to find |
| 375 | // it in the theme! |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 376 | ssize_t new_block = |
| 377 | theme->getAttribute(cur_ident, &value, &type_set_flags); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 378 | if (new_block >= 0) { |
| 379 | if (kDebugStyles) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 380 | ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, |
| 381 | value.data); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 382 | } |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 383 | new_block = res.resolveReference(&value, new_block, &resid, |
| 384 | &type_set_flags, &config); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 385 | if (new_block >= 0) { |
| 386 | block = new_block; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 387 | } |
| 388 | |
| 389 | if (kDebugStyles) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 390 | ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, |
| 391 | value.data); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 392 | } |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 393 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 394 | } |
| 395 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 404 | } |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 405 | |
| 406 | if (kDebugStyles) { |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 407 | ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, |
| 408 | value.dataType, value.data); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 409 | } |
| 410 | |
| 411 | // Write the final value back to Java. |
| 412 | out_values[STYLE_TYPE] = value.dataType; |
| 413 | out_values[STYLE_DATA] = value.data; |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 414 | out_values[STYLE_ASSET_COOKIE] = |
| 415 | block != kXmlBlock ? static_cast<uint32_t>(res.getTableCookie(block)) |
| 416 | : static_cast<uint32_t>(-1); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 417 | out_values[STYLE_RESOURCE_ID] = resid; |
| 418 | out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; |
| 419 | out_values[STYLE_DENSITY] = config.density; |
| 420 | |
Adam Lesinski | 06d3e8f | 2017-01-05 17:03:55 -0800 | [diff] [blame] | 421 | if (value.dataType != Res_value::TYPE_NULL) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 422 | indices_idx++; |
Adam Lesinski | 06d3e8f | 2017-01-05 17:03:55 -0800 | [diff] [blame] | 423 | |
| 424 | // out_indices must NOT be nullptr. |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 425 | out_indices[indices_idx] = ii; |
| 426 | } |
| 427 | |
| 428 | out_values += STYLE_NUM_ENTRIES; |
| 429 | } |
| 430 | |
| 431 | res.unlock(); |
| 432 | |
Adam Lesinski | 06d3e8f | 2017-01-05 17:03:55 -0800 | [diff] [blame] | 433 | // out_indices must NOT be nullptr. |
| 434 | out_indices[0] = indices_idx; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 435 | } |
| 436 | |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 437 | bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, |
| 438 | uint32_t* attrs, size_t attrs_length, |
| 439 | uint32_t* out_values, uint32_t* out_indices) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 440 | ResTable_config config; |
| 441 | Res_value value; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 442 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 443 | int indices_idx = 0; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 444 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 445 | // Now lock down the resource object and start pulling stuff from it. |
| 446 | res->lock(); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 447 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 452 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 453 | static const ssize_t kXmlBlock = 0x10000000; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 454 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 461 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 462 | value.dataType = Res_value::TYPE_NULL; |
| 463 | value.data = Res_value::DATA_NULL_UNDEFINED; |
| 464 | config.density = 0; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 465 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 477 | } |
| 478 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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"); |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 483 | ssize_t new_block = res->resolveReference(&value, block, &resid, |
| 484 | &type_set_flags, &config); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 485 | if (new_block >= 0) block = new_block; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 486 | } |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 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; |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 498 | out_values[STYLE_ASSET_COOKIE] = |
| 499 | block != kXmlBlock ? static_cast<uint32_t>(res->getTableCookie(block)) |
| 500 | : static_cast<uint32_t>(-1); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 501 | out_values[STYLE_RESOURCE_ID] = resid; |
| 502 | out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; |
| 503 | out_values[STYLE_DENSITY] = config.density; |
| 504 | |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 505 | if (out_indices != nullptr && value.dataType != Res_value::TYPE_NULL) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 506 | indices_idx++; |
| 507 | out_indices[indices_idx] = ii; |
| 508 | } |
| 509 | |
| 510 | out_values += STYLE_NUM_ENTRIES; |
| 511 | } |
| 512 | |
| 513 | res->unlock(); |
| 514 | |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 515 | if (out_indices != nullptr) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 516 | out_indices[0] = indices_idx; |
| 517 | } |
| 518 | return true; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 519 | } |
| 520 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 521 | } // namespace android |