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 | |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 23 | #include "androidfw/AssetManager2.h" |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 24 | #include "androidfw/AttributeFinder.h" |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 25 | |
| 26 | constexpr bool kDebugStyles = false; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 27 | #define DEBUG_LOG(...) do { if (kDebugStyles) { ALOGI(__VA_ARGS__); } } while(0) |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 28 | |
| 29 | namespace android { |
| 30 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 31 | namespace { |
| 32 | |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 33 | // Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. |
| 34 | static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { |
| 35 | return cookie != kInvalidCookie ? static_cast<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1); |
| 36 | } |
| 37 | |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 38 | class XmlAttributeFinder |
| 39 | : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 40 | public: |
| 41 | explicit XmlAttributeFinder(const ResXMLParser* parser) |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 42 | : BackTrackingAttributeFinder( |
| 43 | 0, parser != nullptr ? parser->getAttributeCount() : 0), |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 44 | parser_(parser) {} |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 45 | |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 46 | inline uint32_t GetAttribute(size_t index) const { |
| 47 | return parser_->getAttributeNameResID(index); |
| 48 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 49 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 50 | private: |
| 51 | const ResXMLParser* parser_; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 52 | }; |
| 53 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 54 | class BagAttributeFinder |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 55 | : public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 56 | public: |
Chih-Hung Hsieh | c2ace0c | 2018-12-20 13:46:53 -0800 | [diff] [blame] | 57 | explicit BagAttributeFinder(const ResolvedBag* bag) |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 58 | : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, |
| 59 | bag != nullptr ? bag->entries + bag->entry_count : nullptr) { |
| 60 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 61 | |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 62 | inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { |
| 63 | return entry->key; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 64 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 65 | }; |
| 66 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 67 | base::expected<const ResolvedBag*, NullOrIOError> GetStyleBag(Theme* theme, |
| 68 | uint32_t theme_attribute_resid, |
| 69 | uint32_t fallback_resid, |
| 70 | uint32_t* out_theme_flags) { |
| 71 | // Load the style from the attribute if specified. |
| 72 | if (theme_attribute_resid != 0U) { |
| 73 | std::optional<AssetManager2::SelectedValue> value = theme->GetAttribute(theme_attribute_resid); |
| 74 | if (value.has_value()) { |
| 75 | *out_theme_flags |= value->flags; |
| 76 | auto result = theme->GetAssetManager()->ResolveBag(*value); |
| 77 | if (result.has_value() || IsIOError(result)) { |
| 78 | return result; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 79 | } |
| 80 | } |
| 81 | } |
| 82 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 83 | // Fallback to loading the style from the resource id if specified. |
| 84 | if (fallback_resid != 0U) { |
| 85 | return theme->GetAssetManager()->GetBag(fallback_resid); |
| 86 | } |
| 87 | |
| 88 | return base::unexpected(std::nullopt); |
| 89 | } |
| 90 | |
| 91 | base::expected<const ResolvedBag*, NullOrIOError> GetXmlStyleBag(Theme* theme, |
| 92 | ResXMLParser* xml_parser, |
| 93 | uint32_t* out_theme_flags) { |
| 94 | if (xml_parser == nullptr) { |
| 95 | return base::unexpected(std::nullopt); |
| 96 | } |
| 97 | |
| 98 | // Retrieve the style resource ID associated with the current XML tag's style attribute. |
| 99 | Res_value value; |
| 100 | const ssize_t idx = xml_parser->indexOfStyle(); |
| 101 | if (idx < 0 || xml_parser->getAttributeValue(idx, &value) < 0) { |
| 102 | return base::unexpected(std::nullopt); |
| 103 | } |
| 104 | |
| 105 | if (value.dataType == Res_value::TYPE_ATTRIBUTE) { |
| 106 | // Resolve the attribute with out theme. |
| 107 | if (std::optional<AssetManager2::SelectedValue> result = theme->GetAttribute(value.data)) { |
| 108 | *out_theme_flags |= result->flags; |
| 109 | return theme->GetAssetManager()->ResolveBag(*result); |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 110 | } |
| 111 | } |
| 112 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 113 | if (value.dataType == Res_value::TYPE_REFERENCE) { |
| 114 | return theme->GetAssetManager()->GetBag(value.data); |
| 115 | } |
| 116 | |
| 117 | return base::unexpected(std::nullopt); |
| 118 | } |
| 119 | |
| 120 | } // namespace |
| 121 | |
| 122 | base::expected<std::monostate, IOError> ResolveAttrs(Theme* theme, uint32_t def_style_attr, |
| 123 | uint32_t def_style_res, uint32_t* src_values, |
| 124 | size_t src_values_length, uint32_t* attrs, |
| 125 | size_t attrs_length, uint32_t* out_values, |
| 126 | uint32_t* out_indices) { |
| 127 | DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, |
| 128 | def_style_res); |
| 129 | |
| 130 | int indices_idx = 0; |
| 131 | const AssetManager2* assetmanager = theme->GetAssetManager(); |
| 132 | |
| 133 | // Load default style from attribute or resource id, if specified... |
| 134 | uint32_t def_style_theme_flags = 0U; |
| 135 | const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_res, |
| 136 | &def_style_theme_flags); |
| 137 | if (UNLIKELY(IsIOError(default_style_bag))) { |
| 138 | return base::unexpected(GetIOError(default_style_bag.error())); |
| 139 | } |
| 140 | |
| 141 | BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr)); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 142 | |
| 143 | // Now iterate through all of the attributes that the client has requested, |
| 144 | // filling in each with whatever data we can find. |
| 145 | for (size_t ii = 0; ii < attrs_length; ii++) { |
| 146 | const uint32_t cur_ident = attrs[ii]; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 147 | DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 148 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 149 | // Try to find a value for this attribute... we prioritize values |
| 150 | // coming from, first XML attributes, then XML style, then default |
| 151 | // style, and finally the theme. |
| 152 | |
| 153 | // Retrieve the current input value if available. |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 154 | AssetManager2::SelectedValue value{}; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 155 | if (src_values_length > 0 && src_values[ii] != 0) { |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 156 | value.type = Res_value::TYPE_ATTRIBUTE; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 157 | value.data = src_values[ii]; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 158 | DEBUG_LOG("-> From values: type=0x%x, data=0x%08x", value.type, value.data); |
Adam Lesinski | 32e7501 | 2017-05-09 15:25:37 -0700 | [diff] [blame] | 159 | } else { |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 160 | const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); |
| 161 | if (entry != def_style_attr_finder.end()) { |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 162 | value = AssetManager2::SelectedValue(*default_style_bag, *entry); |
| 163 | value.flags |= def_style_theme_flags; |
| 164 | DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x", value.type, value.data); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 165 | } |
| 166 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 167 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 168 | if (value.type != Res_value::TYPE_NULL) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 169 | // Take care of resolving the found resource to its final value. |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 170 | const auto result = theme->ResolveAttributeReference(value); |
| 171 | if (UNLIKELY(IsIOError(result))) { |
| 172 | return base::unexpected(GetIOError(result.error())); |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 173 | } |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 174 | DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data); |
Adam Lesinski | 32e7501 | 2017-05-09 15:25:37 -0700 | [diff] [blame] | 175 | } else if (value.data != Res_value::DATA_NULL_EMPTY) { |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 176 | // If we still don't have a value for this attribute, try to find it in the theme! |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 177 | if (auto attr_value = theme->GetAttribute(cur_ident)) { |
| 178 | value = *attr_value; |
| 179 | DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data); |
| 180 | |
| 181 | const auto result = assetmanager->ResolveReference(value); |
| 182 | if (UNLIKELY(IsIOError(result))) { |
| 183 | return base::unexpected(GetIOError(result.error())); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 184 | } |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 185 | DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 186 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 187 | } |
| 188 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 189 | // Deal with the special @null value -- it turns back to TYPE_NULL. |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 190 | if (value.type == Res_value::TYPE_REFERENCE && value.data == 0) { |
| 191 | DEBUG_LOG("-> Setting to @null!"); |
| 192 | value.type = Res_value::TYPE_NULL; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 193 | value.data = Res_value::DATA_NULL_UNDEFINED; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 194 | value.cookie = kInvalidCookie; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 195 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 196 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 197 | DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 198 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 199 | // Write the final value back to Java. |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 200 | out_values[STYLE_TYPE] = value.type; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 201 | out_values[STYLE_DATA] = value.data; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 202 | out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie); |
| 203 | out_values[STYLE_RESOURCE_ID] = value.resid; |
| 204 | out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags; |
| 205 | out_values[STYLE_DENSITY] = value.config.density; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 206 | |
Adam Lesinski | 32e7501 | 2017-05-09 15:25:37 -0700 | [diff] [blame] | 207 | if (out_indices != nullptr && |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 208 | (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) { |
| 209 | out_indices[++indices_idx] = ii; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 210 | } |
| 211 | |
| 212 | out_values += STYLE_NUM_ENTRIES; |
| 213 | } |
| 214 | |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 215 | if (out_indices != nullptr) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 216 | out_indices[0] = indices_idx; |
| 217 | } |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 218 | return {}; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 219 | } |
| 220 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 221 | base::expected<std::monostate, IOError> ApplyStyle(Theme* theme, ResXMLParser* xml_parser, |
| 222 | uint32_t def_style_attr, |
| 223 | uint32_t def_style_resid, |
| 224 | const uint32_t* attrs, size_t attrs_length, |
| 225 | uint32_t* out_values, uint32_t* out_indices) { |
| 226 | DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, |
| 227 | def_style_attr, def_style_resid, xml_parser); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 228 | |
| 229 | int indices_idx = 0; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 230 | const AssetManager2* assetmanager = theme->GetAssetManager(); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 231 | |
| 232 | // Load default style from attribute, if specified... |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 233 | uint32_t def_style_theme_flags = 0U; |
| 234 | const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_resid, |
| 235 | &def_style_theme_flags); |
| 236 | if (IsIOError(default_style_bag)) { |
| 237 | return base::unexpected(GetIOError(default_style_bag.error())); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 238 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 239 | |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 240 | // Retrieve the style resource ID associated with the current XML tag's style attribute. |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 241 | uint32_t xml_style_theme_flags = 0U; |
| 242 | const auto xml_style_bag = GetXmlStyleBag(theme, xml_parser, &def_style_theme_flags); |
| 243 | if (IsIOError(xml_style_bag)) { |
| 244 | return base::unexpected(GetIOError(xml_style_bag.error())); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 245 | } |
| 246 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 247 | BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr)); |
| 248 | BagAttributeFinder xml_style_attr_finder(xml_style_bag.value_or(nullptr)); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 249 | XmlAttributeFinder xml_attr_finder(xml_parser); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 250 | |
| 251 | // Now iterate through all of the attributes that the client has requested, |
| 252 | // filling in each with whatever data we can find. |
| 253 | for (size_t ii = 0; ii < attrs_length; ii++) { |
| 254 | const uint32_t cur_ident = attrs[ii]; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 255 | DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 256 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 257 | AssetManager2::SelectedValue value{}; |
Aurimas Liutikas | 949b05d | 2019-01-30 17:20:41 -0800 | [diff] [blame] | 258 | uint32_t value_source_resid = 0; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 259 | |
| 260 | // Try to find a value for this attribute... we prioritize values |
| 261 | // coming from, first XML attributes, then XML style, then default |
| 262 | // style, and finally the theme. |
| 263 | |
| 264 | // Walk through the xml attributes looking for the requested attribute. |
| 265 | const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 266 | if (xml_attr_idx != xml_attr_finder.end()) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 267 | // We found the attribute we were looking for. |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 268 | Res_value attribute_value; |
| 269 | xml_parser->getAttributeValue(xml_attr_idx, &attribute_value); |
| 270 | value.type = attribute_value.dataType; |
| 271 | value.data = attribute_value.data; |
Aurimas Liutikas | 949b05d | 2019-01-30 17:20:41 -0800 | [diff] [blame] | 272 | value_source_resid = xml_parser->getSourceResourceId(); |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 273 | DEBUG_LOG("-> From XML: type=0x%x, data=0x%08x", value.type, value.data); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 274 | } |
| 275 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 276 | if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { |
Adam Lesinski | 32e7501 | 2017-05-09 15:25:37 -0700 | [diff] [blame] | 277 | // Walk through the style class values looking for the requested attribute. |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 278 | const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); |
| 279 | if (entry != xml_style_attr_finder.end()) { |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 280 | value = AssetManager2::SelectedValue(*xml_style_bag, *entry); |
| 281 | value.flags |= xml_style_theme_flags; |
Aurimas Liutikas | 949b05d | 2019-01-30 17:20:41 -0800 | [diff] [blame] | 282 | value_source_resid = entry->style; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 283 | DEBUG_LOG("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data, |
| 284 | value_source_resid); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 285 | } |
| 286 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 287 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 288 | if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { |
Adam Lesinski | 32e7501 | 2017-05-09 15:25:37 -0700 | [diff] [blame] | 289 | // Walk through the default style values looking for the requested attribute. |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 290 | const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); |
| 291 | if (entry != def_style_attr_finder.end()) { |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 292 | value = AssetManager2::SelectedValue(*default_style_bag, *entry); |
| 293 | value.flags |= def_style_theme_flags; |
Aurimas Liutikas | 949b05d | 2019-01-30 17:20:41 -0800 | [diff] [blame] | 294 | value_source_resid = entry->style; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 295 | DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data, |
| 296 | entry->style); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 297 | } |
| 298 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 299 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 300 | if (value.type != Res_value::TYPE_NULL) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 301 | // Take care of resolving the found resource to its final value. |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 302 | auto result = theme->ResolveAttributeReference(value); |
| 303 | if (UNLIKELY(IsIOError(result))) { |
| 304 | return base::unexpected(GetIOError(result.error())); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 305 | } |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 306 | DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data); |
Adam Lesinski | 32e7501 | 2017-05-09 15:25:37 -0700 | [diff] [blame] | 307 | } else if (value.data != Res_value::DATA_NULL_EMPTY) { |
| 308 | // If we still don't have a value for this attribute, try to find it in the theme! |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 309 | if (auto attr_value = theme->GetAttribute(cur_ident)) { |
| 310 | value = *attr_value; |
| 311 | DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 312 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 313 | auto result = assetmanager->ResolveReference(value); |
| 314 | if (UNLIKELY(IsIOError(result))) { |
| 315 | return base::unexpected(GetIOError(result.error())); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 316 | } |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 317 | DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data); |
| 318 | // TODO: set value_source_resid for the style in the theme that was used. |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 319 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 320 | } |
| 321 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 322 | // Deal with the special @null value -- it turns back to TYPE_NULL. |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 323 | if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) { |
| 324 | DEBUG_LOG("-> Setting to @null!"); |
| 325 | value.type = Res_value::TYPE_NULL; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 326 | value.data = Res_value::DATA_NULL_UNDEFINED; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 327 | value.cookie = kInvalidCookie; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 328 | } |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 329 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 330 | DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 331 | |
| 332 | // Write the final value back to Java. |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 333 | out_values[STYLE_TYPE] = value.type; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 334 | out_values[STYLE_DATA] = value.data; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 335 | out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie); |
| 336 | out_values[STYLE_RESOURCE_ID] = value.resid; |
| 337 | out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags; |
| 338 | out_values[STYLE_DENSITY] = value.config.density; |
Aurimas Liutikas | f9dbd5f | 2019-03-07 11:21:51 -0800 | [diff] [blame] | 339 | out_values[STYLE_SOURCE_RESOURCE_ID] = value_source_resid; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 340 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 341 | if (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) { |
Adam Lesinski | 06d3e8f | 2017-01-05 17:03:55 -0800 | [diff] [blame] | 342 | // out_indices must NOT be nullptr. |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 343 | out_indices[++indices_idx] = ii; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 344 | } |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 345 | out_values += STYLE_NUM_ENTRIES; |
| 346 | } |
| 347 | |
Adam Lesinski | 06d3e8f | 2017-01-05 17:03:55 -0800 | [diff] [blame] | 348 | // out_indices must NOT be nullptr. |
| 349 | out_indices[0] = indices_idx; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 350 | return {}; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 351 | } |
| 352 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 353 | base::expected<std::monostate, IOError> RetrieveAttributes(AssetManager2* assetmanager, |
| 354 | ResXMLParser* xml_parser, |
| 355 | uint32_t* attrs, |
| 356 | size_t attrs_length, |
| 357 | uint32_t* out_values, |
| 358 | uint32_t* out_indices) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 359 | int indices_idx = 0; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 360 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 361 | // Retrieve the XML attributes, if requested. |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 362 | size_t ix = 0; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 363 | const size_t xml_attr_count = xml_parser->getAttributeCount(); |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 364 | uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 365 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 366 | // Now iterate through all of the attributes that the client has requested, |
| 367 | // filling in each with whatever data we can find. |
| 368 | for (size_t ii = 0; ii < attrs_length; ii++) { |
| 369 | const uint32_t cur_ident = attrs[ii]; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 370 | AssetManager2::SelectedValue value{}; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 371 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 372 | // Try to find a value for this attribute... |
| 373 | // Skip through XML attributes until the end or the next possible match. |
| 374 | while (ix < xml_attr_count && cur_ident > cur_xml_attr) { |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 375 | cur_xml_attr = xml_parser->getAttributeNameResID(++ix); |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 376 | } |
| 377 | |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 378 | // Retrieve the current XML attribute if it matches, and step to next. |
| 379 | if (ix < xml_attr_count && cur_ident == cur_xml_attr) { |
| 380 | Res_value attribute_value; |
| 381 | xml_parser->getAttributeValue(ix, &attribute_value); |
| 382 | value.type = attribute_value.dataType; |
| 383 | value.data = attribute_value.data; |
| 384 | cur_xml_attr = xml_parser->getAttributeNameResID(++ix); |
| 385 | } |
| 386 | |
| 387 | if (value.type != Res_value::TYPE_NULL) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 388 | // Take care of resolving the found resource to its final value. |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 389 | auto result = assetmanager->ResolveReference(value); |
| 390 | if (UNLIKELY(IsIOError(result))) { |
| 391 | return base::unexpected(GetIOError(result.error())); |
Adam Lesinski | bebfcc4 | 2018-02-12 14:27:46 -0800 | [diff] [blame] | 392 | } |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 393 | } |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 394 | |
| 395 | // Deal with the special @null value -- it turns back to TYPE_NULL. |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 396 | if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) { |
| 397 | value.type = Res_value::TYPE_NULL; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 398 | value.data = Res_value::DATA_NULL_UNDEFINED; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 399 | value.cookie = kInvalidCookie; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 400 | } |
| 401 | |
| 402 | // Write the final value back to Java. |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 403 | out_values[STYLE_TYPE] = value.type; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 404 | out_values[STYLE_DATA] = value.data; |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 405 | out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie); |
| 406 | out_values[STYLE_RESOURCE_ID] = value.resid; |
| 407 | out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags; |
| 408 | out_values[STYLE_DENSITY] = value.config.density; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 409 | |
Adam Lesinski | 32e7501 | 2017-05-09 15:25:37 -0700 | [diff] [blame] | 410 | if (out_indices != nullptr && |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 411 | (value.type != Res_value::TYPE_NULL || |
| 412 | value.data == Res_value::DATA_NULL_EMPTY)) { |
| 413 | out_indices[++indices_idx] = ii; |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 414 | } |
| 415 | |
| 416 | out_values += STYLE_NUM_ENTRIES; |
| 417 | } |
| 418 | |
Adam Lesinski | 4c67a47 | 2016-11-10 16:43:59 -0800 | [diff] [blame] | 419 | if (out_indices != nullptr) { |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 420 | out_indices[0] = indices_idx; |
| 421 | } |
Ryan Mitchell | c75c2e0 | 2020-08-17 08:42:48 -0700 | [diff] [blame^] | 422 | return {}; |
Adam Lesinski | 4452e13 | 2016-10-12 07:47:28 -0700 | [diff] [blame] | 423 | } |
| 424 | |
Adam Lesinski | 7a37b74 | 2016-10-12 14:05:55 -0700 | [diff] [blame] | 425 | } // namespace android |