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