1 /*
2 * Copyright (C) 2008 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 #define LOG_TAG "ResourceType"
18 //#define LOG_NDEBUG 0
19
20 #include <ctype.h>
21 #include <memory.h>
22 #include <stddef.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <algorithm>
28 #include <limits>
29 #include <map>
30 #include <memory>
31 #include <set>
32 #include <type_traits>
33
34 #include <android-base/macros.h>
35 #include <androidfw/ByteBucketArray.h>
36 #include <androidfw/ResourceTypes.h>
37 #include <androidfw/TypeWrappers.h>
38 #include <cutils/atomic.h>
39 #include <utils/ByteOrder.h>
40 #include <utils/Debug.h>
41 #include <utils/Log.h>
42 #include <utils/String16.h>
43 #include <utils/String8.h>
44
45 #ifdef __ANDROID__
46 #include <binder/TextOutput.h>
47 #endif
48
49 #ifndef INT32_MAX
50 #define INT32_MAX ((int32_t)(2147483647))
51 #endif
52
53 namespace android {
54
55 #if defined(_WIN32)
56 #undef nhtol
57 #undef htonl
58 #define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) )
59 #define htonl(x) ntohl(x)
60 #define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) )
61 #define htons(x) ntohs(x)
62 #endif
63
64 #define IDMAP_MAGIC 0x504D4449
65
66 #define APP_PACKAGE_ID 0x7f
67 #define SYS_PACKAGE_ID 0x01
68
69 static const bool kDebugStringPoolNoisy = false;
70 static const bool kDebugXMLNoisy = false;
71 static const bool kDebugTableNoisy = false;
72 static const bool kDebugTableGetEntry = false;
73 static const bool kDebugTableSuperNoisy = false;
74 static const bool kDebugLoadTableNoisy = false;
75 static const bool kDebugLoadTableSuperNoisy = false;
76 static const bool kDebugTableTheme = false;
77 static const bool kDebugResXMLTree = false;
78 static const bool kDebugLibNoisy = false;
79
80 // TODO: This code uses 0xFFFFFFFF converted to bag_set* as a sentinel value. This is bad practice.
81
82 // Standard C isspace() is only required to look at the low byte of its input, so
83 // produces incorrect results for UTF-16 characters. For safety's sake, assume that
84 // any high-byte UTF-16 code point is not whitespace.
isspace16(char16_t c)85 inline int isspace16(char16_t c) {
86 return (c < 0x0080 && isspace(c));
87 }
88
89 template<typename T>
max(T a,T b)90 inline static T max(T a, T b) {
91 return a > b ? a : b;
92 }
93
94 // range checked; guaranteed to NUL-terminate within the stated number of available slots
95 // NOTE: if this truncates the dst string due to running out of space, no attempt is
96 // made to avoid splitting surrogate pairs.
strcpy16_dtoh(char16_t * dst,const uint16_t * src,size_t avail)97 static void strcpy16_dtoh(char16_t* dst, const uint16_t* src, size_t avail)
98 {
99 char16_t* last = dst + avail - 1;
100 while (*src && (dst < last)) {
101 char16_t s = dtohs(static_cast<char16_t>(*src));
102 *dst++ = s;
103 src++;
104 }
105 *dst = 0;
106 }
107
validate_chunk(const ResChunk_header * chunk,size_t minSize,const uint8_t * dataEnd,const char * name)108 static status_t validate_chunk(const ResChunk_header* chunk,
109 size_t minSize,
110 const uint8_t* dataEnd,
111 const char* name)
112 {
113 const uint16_t headerSize = dtohs(chunk->headerSize);
114 const uint32_t size = dtohl(chunk->size);
115
116 if (headerSize >= minSize) {
117 if (headerSize <= size) {
118 if (((headerSize|size)&0x3) == 0) {
119 if ((size_t)size <= (size_t)(dataEnd-((const uint8_t*)chunk))) {
120 return NO_ERROR;
121 }
122 ALOGW("%s data size 0x%x extends beyond resource end %p.",
123 name, size, (void*)(dataEnd-((const uint8_t*)chunk)));
124 return BAD_TYPE;
125 }
126 ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.",
127 name, (int)size, (int)headerSize);
128 return BAD_TYPE;
129 }
130 ALOGW("%s size 0x%x is smaller than header size 0x%x.",
131 name, size, headerSize);
132 return BAD_TYPE;
133 }
134 ALOGW("%s header size 0x%04x is too small.",
135 name, headerSize);
136 return BAD_TYPE;
137 }
138
fill9patchOffsets(Res_png_9patch * patch)139 static void fill9patchOffsets(Res_png_9patch* patch) {
140 patch->xDivsOffset = sizeof(Res_png_9patch);
141 patch->yDivsOffset = patch->xDivsOffset + (patch->numXDivs * sizeof(int32_t));
142 patch->colorsOffset = patch->yDivsOffset + (patch->numYDivs * sizeof(int32_t));
143 }
144
copyFrom_dtoh(const Res_value & src)145 void Res_value::copyFrom_dtoh(const Res_value& src)
146 {
147 size = dtohs(src.size);
148 res0 = src.res0;
149 dataType = src.dataType;
150 data = dtohl(src.data);
151 }
152
deviceToFile()153 void Res_png_9patch::deviceToFile()
154 {
155 int32_t* xDivs = getXDivs();
156 for (int i = 0; i < numXDivs; i++) {
157 xDivs[i] = htonl(xDivs[i]);
158 }
159 int32_t* yDivs = getYDivs();
160 for (int i = 0; i < numYDivs; i++) {
161 yDivs[i] = htonl(yDivs[i]);
162 }
163 paddingLeft = htonl(paddingLeft);
164 paddingRight = htonl(paddingRight);
165 paddingTop = htonl(paddingTop);
166 paddingBottom = htonl(paddingBottom);
167 uint32_t* colors = getColors();
168 for (int i=0; i<numColors; i++) {
169 colors[i] = htonl(colors[i]);
170 }
171 }
172
fileToDevice()173 void Res_png_9patch::fileToDevice()
174 {
175 int32_t* xDivs = getXDivs();
176 for (int i = 0; i < numXDivs; i++) {
177 xDivs[i] = ntohl(xDivs[i]);
178 }
179 int32_t* yDivs = getYDivs();
180 for (int i = 0; i < numYDivs; i++) {
181 yDivs[i] = ntohl(yDivs[i]);
182 }
183 paddingLeft = ntohl(paddingLeft);
184 paddingRight = ntohl(paddingRight);
185 paddingTop = ntohl(paddingTop);
186 paddingBottom = ntohl(paddingBottom);
187 uint32_t* colors = getColors();
188 for (int i=0; i<numColors; i++) {
189 colors[i] = ntohl(colors[i]);
190 }
191 }
192
serializedSize() const193 size_t Res_png_9patch::serializedSize() const
194 {
195 // The size of this struct is 32 bytes on the 32-bit target system
196 // 4 * int8_t
197 // 4 * int32_t
198 // 3 * uint32_t
199 return 32
200 + numXDivs * sizeof(int32_t)
201 + numYDivs * sizeof(int32_t)
202 + numColors * sizeof(uint32_t);
203 }
204
serialize(const Res_png_9patch & patch,const int32_t * xDivs,const int32_t * yDivs,const uint32_t * colors)205 void* Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs,
206 const int32_t* yDivs, const uint32_t* colors)
207 {
208 // Use calloc since we're going to leave a few holes in the data
209 // and want this to run cleanly under valgrind
210 void* newData = calloc(1, patch.serializedSize());
211 serialize(patch, xDivs, yDivs, colors, newData);
212 return newData;
213 }
214
serialize(const Res_png_9patch & patch,const int32_t * xDivs,const int32_t * yDivs,const uint32_t * colors,void * outData)215 void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs,
216 const int32_t* yDivs, const uint32_t* colors, void* outData)
217 {
218 uint8_t* data = (uint8_t*) outData;
219 memcpy(data, &patch.wasDeserialized, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors
220 memcpy(data + 12, &patch.paddingLeft, 16); // copy paddingXXXX
221 data += 32;
222
223 memcpy(data, xDivs, patch.numXDivs * sizeof(int32_t));
224 data += patch.numXDivs * sizeof(int32_t);
225 memcpy(data, yDivs, patch.numYDivs * sizeof(int32_t));
226 data += patch.numYDivs * sizeof(int32_t);
227 memcpy(data, colors, patch.numColors * sizeof(uint32_t));
228
229 fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData));
230 }
231
assertIdmapHeader(const void * idmap,size_t size)232 static bool assertIdmapHeader(const void* idmap, size_t size) {
233 if (reinterpret_cast<uintptr_t>(idmap) & 0x03) {
234 ALOGE("idmap: header is not word aligned");
235 return false;
236 }
237
238 if (size < ResTable::IDMAP_HEADER_SIZE_BYTES) {
239 ALOGW("idmap: header too small (%d bytes)", (uint32_t) size);
240 return false;
241 }
242
243 const uint32_t magic = htodl(*reinterpret_cast<const uint32_t*>(idmap));
244 if (magic != IDMAP_MAGIC) {
245 ALOGW("idmap: no magic found in header (is 0x%08x, expected 0x%08x)",
246 magic, IDMAP_MAGIC);
247 return false;
248 }
249
250 const uint32_t version = htodl(*(reinterpret_cast<const uint32_t*>(idmap) + 1));
251 if (version != ResTable::IDMAP_CURRENT_VERSION) {
252 // We are strict about versions because files with this format are
253 // auto-generated and don't need backwards compatibility.
254 ALOGW("idmap: version mismatch in header (is 0x%08x, expected 0x%08x)",
255 version, ResTable::IDMAP_CURRENT_VERSION);
256 return false;
257 }
258 return true;
259 }
260
261 class IdmapEntries {
262 public:
IdmapEntries()263 IdmapEntries() : mData(NULL) {}
264
hasEntries() const265 bool hasEntries() const {
266 if (mData == NULL) {
267 return false;
268 }
269
270 return (dtohs(*mData) > 0);
271 }
272
byteSize() const273 size_t byteSize() const {
274 if (mData == NULL) {
275 return 0;
276 }
277 uint16_t entryCount = dtohs(mData[2]);
278 return (sizeof(uint16_t) * 4) + (sizeof(uint32_t) * static_cast<size_t>(entryCount));
279 }
280
targetTypeId() const281 uint8_t targetTypeId() const {
282 if (mData == NULL) {
283 return 0;
284 }
285 return dtohs(mData[0]);
286 }
287
overlayTypeId() const288 uint8_t overlayTypeId() const {
289 if (mData == NULL) {
290 return 0;
291 }
292 return dtohs(mData[1]);
293 }
294
setTo(const void * entryHeader,size_t size)295 status_t setTo(const void* entryHeader, size_t size) {
296 if (reinterpret_cast<uintptr_t>(entryHeader) & 0x03) {
297 ALOGE("idmap: entry header is not word aligned");
298 return UNKNOWN_ERROR;
299 }
300
301 if (size < sizeof(uint16_t) * 4) {
302 ALOGE("idmap: entry header is too small (%u bytes)", (uint32_t) size);
303 return UNKNOWN_ERROR;
304 }
305
306 const uint16_t* header = reinterpret_cast<const uint16_t*>(entryHeader);
307 const uint16_t targetTypeId = dtohs(header[0]);
308 const uint16_t overlayTypeId = dtohs(header[1]);
309 if (targetTypeId == 0 || overlayTypeId == 0 || targetTypeId > 255 || overlayTypeId > 255) {
310 ALOGE("idmap: invalid type map (%u -> %u)", targetTypeId, overlayTypeId);
311 return UNKNOWN_ERROR;
312 }
313
314 uint16_t entryCount = dtohs(header[2]);
315 if (size < sizeof(uint32_t) * (entryCount + 2)) {
316 ALOGE("idmap: too small (%u bytes) for the number of entries (%u)",
317 (uint32_t) size, (uint32_t) entryCount);
318 return UNKNOWN_ERROR;
319 }
320 mData = header;
321 return NO_ERROR;
322 }
323
lookup(uint16_t entryId,uint16_t * outEntryId) const324 status_t lookup(uint16_t entryId, uint16_t* outEntryId) const {
325 uint16_t entryCount = dtohs(mData[2]);
326 uint16_t offset = dtohs(mData[3]);
327
328 if (entryId < offset) {
329 // The entry is not present in this idmap
330 return BAD_INDEX;
331 }
332
333 entryId -= offset;
334
335 if (entryId >= entryCount) {
336 // The entry is not present in this idmap
337 return BAD_INDEX;
338 }
339
340 // It is safe to access the type here without checking the size because
341 // we have checked this when it was first loaded.
342 const uint32_t* entries = reinterpret_cast<const uint32_t*>(mData) + 2;
343 uint32_t mappedEntry = dtohl(entries[entryId]);
344 if (mappedEntry == 0xffffffff) {
345 // This entry is not present in this idmap
346 return BAD_INDEX;
347 }
348 *outEntryId = static_cast<uint16_t>(mappedEntry);
349 return NO_ERROR;
350 }
351
352 private:
353 const uint16_t* mData;
354 };
355
parseIdmap(const void * idmap,size_t size,uint8_t * outPackageId,KeyedVector<uint8_t,IdmapEntries> * outMap)356 status_t parseIdmap(const void* idmap, size_t size, uint8_t* outPackageId, KeyedVector<uint8_t, IdmapEntries>* outMap) {
357 if (!assertIdmapHeader(idmap, size)) {
358 return UNKNOWN_ERROR;
359 }
360
361 size -= ResTable::IDMAP_HEADER_SIZE_BYTES;
362 if (size < sizeof(uint16_t) * 2) {
363 ALOGE("idmap: too small to contain any mapping");
364 return UNKNOWN_ERROR;
365 }
366
367 const uint16_t* data = reinterpret_cast<const uint16_t*>(
368 reinterpret_cast<const uint8_t*>(idmap) + ResTable::IDMAP_HEADER_SIZE_BYTES);
369
370 uint16_t targetPackageId = dtohs(*(data++));
371 if (targetPackageId == 0 || targetPackageId > 255) {
372 ALOGE("idmap: target package ID is invalid (%02x)", targetPackageId);
373 return UNKNOWN_ERROR;
374 }
375
376 uint16_t mapCount = dtohs(*(data++));
377 if (mapCount == 0) {
378 ALOGE("idmap: no mappings");
379 return UNKNOWN_ERROR;
380 }
381
382 if (mapCount > 255) {
383 ALOGW("idmap: too many mappings. Only 255 are possible but %u are present", (uint32_t) mapCount);
384 }
385
386 while (size > sizeof(uint16_t) * 4) {
387 IdmapEntries entries;
388 status_t err = entries.setTo(data, size);
389 if (err != NO_ERROR) {
390 return err;
391 }
392
393 ssize_t index = outMap->add(entries.overlayTypeId(), entries);
394 if (index < 0) {
395 return NO_MEMORY;
396 }
397
398 data += entries.byteSize() / sizeof(uint16_t);
399 size -= entries.byteSize();
400 }
401
402 if (outPackageId != NULL) {
403 *outPackageId = static_cast<uint8_t>(targetPackageId);
404 }
405 return NO_ERROR;
406 }
407
deserialize(void * inData)408 Res_png_9patch* Res_png_9patch::deserialize(void* inData)
409 {
410
411 Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(inData);
412 patch->wasDeserialized = true;
413 fill9patchOffsets(patch);
414
415 return patch;
416 }
417
418 // --------------------------------------------------------------------
419 // --------------------------------------------------------------------
420 // --------------------------------------------------------------------
421
ResStringPool()422 ResStringPool::ResStringPool()
423 : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
424 {
425 }
426
ResStringPool(const void * data,size_t size,bool copyData)427 ResStringPool::ResStringPool(const void* data, size_t size, bool copyData)
428 : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
429 {
430 setTo(data, size, copyData);
431 }
432
~ResStringPool()433 ResStringPool::~ResStringPool()
434 {
435 uninit();
436 }
437
setToEmpty()438 void ResStringPool::setToEmpty()
439 {
440 uninit();
441
442 mOwnedData = calloc(1, sizeof(ResStringPool_header));
443 ResStringPool_header* header = (ResStringPool_header*) mOwnedData;
444 mSize = 0;
445 mEntries = NULL;
446 mStrings = NULL;
447 mStringPoolSize = 0;
448 mEntryStyles = NULL;
449 mStyles = NULL;
450 mStylePoolSize = 0;
451 mHeader = (const ResStringPool_header*) header;
452 }
453
setTo(const void * data,size_t size,bool copyData)454 status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
455 {
456 if (!data || !size) {
457 return (mError=BAD_TYPE);
458 }
459
460 uninit();
461
462 // The chunk must be at least the size of the string pool header.
463 if (size < sizeof(ResStringPool_header)) {
464 ALOGW("Bad string block: data size %zu is too small to be a string block", size);
465 return (mError=BAD_TYPE);
466 }
467
468 // The data is at least as big as a ResChunk_header, so we can safely validate the other
469 // header fields.
470 // `data + size` is safe because the source of `size` comes from the kernel/filesystem.
471 if (validate_chunk(reinterpret_cast<const ResChunk_header*>(data), sizeof(ResStringPool_header),
472 reinterpret_cast<const uint8_t*>(data) + size,
473 "ResStringPool_header") != NO_ERROR) {
474 ALOGW("Bad string block: malformed block dimensions");
475 return (mError=BAD_TYPE);
476 }
477
478 const bool notDeviceEndian = htods(0xf0) != 0xf0;
479
480 if (copyData || notDeviceEndian) {
481 mOwnedData = malloc(size);
482 if (mOwnedData == NULL) {
483 return (mError=NO_MEMORY);
484 }
485 memcpy(mOwnedData, data, size);
486 data = mOwnedData;
487 }
488
489 // The size has been checked, so it is safe to read the data in the ResStringPool_header
490 // data structure.
491 mHeader = (const ResStringPool_header*)data;
492
493 if (notDeviceEndian) {
494 ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader);
495 h->header.headerSize = dtohs(mHeader->header.headerSize);
496 h->header.type = dtohs(mHeader->header.type);
497 h->header.size = dtohl(mHeader->header.size);
498 h->stringCount = dtohl(mHeader->stringCount);
499 h->styleCount = dtohl(mHeader->styleCount);
500 h->flags = dtohl(mHeader->flags);
501 h->stringsStart = dtohl(mHeader->stringsStart);
502 h->stylesStart = dtohl(mHeader->stylesStart);
503 }
504
505 if (mHeader->header.headerSize > mHeader->header.size
506 || mHeader->header.size > size) {
507 ALOGW("Bad string block: header size %d or total size %d is larger than data size %d\n",
508 (int)mHeader->header.headerSize, (int)mHeader->header.size, (int)size);
509 return (mError=BAD_TYPE);
510 }
511 mSize = mHeader->header.size;
512 mEntries = (const uint32_t*)
513 (((const uint8_t*)data)+mHeader->header.headerSize);
514
515 if (mHeader->stringCount > 0) {
516 if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow?
517 || (mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t)))
518 > size) {
519 ALOGW("Bad string block: entry of %d items extends past data size %d\n",
520 (int)(mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))),
521 (int)size);
522 return (mError=BAD_TYPE);
523 }
524
525 size_t charSize;
526 if (mHeader->flags&ResStringPool_header::UTF8_FLAG) {
527 charSize = sizeof(uint8_t);
528 } else {
529 charSize = sizeof(uint16_t);
530 }
531
532 // There should be at least space for the smallest string
533 // (2 bytes length, null terminator).
534 if (mHeader->stringsStart >= (mSize - sizeof(uint16_t))) {
535 ALOGW("Bad string block: string pool starts at %d, after total size %d\n",
536 (int)mHeader->stringsStart, (int)mHeader->header.size);
537 return (mError=BAD_TYPE);
538 }
539
540 mStrings = (const void*)
541 (((const uint8_t*)data) + mHeader->stringsStart);
542
543 if (mHeader->styleCount == 0) {
544 mStringPoolSize = (mSize - mHeader->stringsStart) / charSize;
545 } else {
546 // check invariant: styles starts before end of data
547 if (mHeader->stylesStart >= (mSize - sizeof(uint16_t))) {
548 ALOGW("Bad style block: style block starts at %d past data size of %d\n",
549 (int)mHeader->stylesStart, (int)mHeader->header.size);
550 return (mError=BAD_TYPE);
551 }
552 // check invariant: styles follow the strings
553 if (mHeader->stylesStart <= mHeader->stringsStart) {
554 ALOGW("Bad style block: style block starts at %d, before strings at %d\n",
555 (int)mHeader->stylesStart, (int)mHeader->stringsStart);
556 return (mError=BAD_TYPE);
557 }
558 mStringPoolSize =
559 (mHeader->stylesStart-mHeader->stringsStart)/charSize;
560 }
561
562 // check invariant: stringCount > 0 requires a string pool to exist
563 if (mStringPoolSize == 0) {
564 ALOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount);
565 return (mError=BAD_TYPE);
566 }
567
568 if (notDeviceEndian) {
569 size_t i;
570 uint32_t* e = const_cast<uint32_t*>(mEntries);
571 for (i=0; i<mHeader->stringCount; i++) {
572 e[i] = dtohl(mEntries[i]);
573 }
574 if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) {
575 const uint16_t* strings = (const uint16_t*)mStrings;
576 uint16_t* s = const_cast<uint16_t*>(strings);
577 for (i=0; i<mStringPoolSize; i++) {
578 s[i] = dtohs(strings[i]);
579 }
580 }
581 }
582
583 if ((mHeader->flags&ResStringPool_header::UTF8_FLAG &&
584 ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) ||
585 (!(mHeader->flags&ResStringPool_header::UTF8_FLAG) &&
586 ((uint16_t*)mStrings)[mStringPoolSize-1] != 0)) {
587 ALOGW("Bad string block: last string is not 0-terminated\n");
588 return (mError=BAD_TYPE);
589 }
590 } else {
591 mStrings = NULL;
592 mStringPoolSize = 0;
593 }
594
595 if (mHeader->styleCount > 0) {
596 mEntryStyles = mEntries + mHeader->stringCount;
597 // invariant: integer overflow in calculating mEntryStyles
598 if (mEntryStyles < mEntries) {
599 ALOGW("Bad string block: integer overflow finding styles\n");
600 return (mError=BAD_TYPE);
601 }
602
603 if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) {
604 ALOGW("Bad string block: entry of %d styles extends past data size %d\n",
605 (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader),
606 (int)size);
607 return (mError=BAD_TYPE);
608 }
609 mStyles = (const uint32_t*)
610 (((const uint8_t*)data)+mHeader->stylesStart);
611 if (mHeader->stylesStart >= mHeader->header.size) {
612 ALOGW("Bad string block: style pool starts %d, after total size %d\n",
613 (int)mHeader->stylesStart, (int)mHeader->header.size);
614 return (mError=BAD_TYPE);
615 }
616 mStylePoolSize =
617 (mHeader->header.size-mHeader->stylesStart)/sizeof(uint32_t);
618
619 if (notDeviceEndian) {
620 size_t i;
621 uint32_t* e = const_cast<uint32_t*>(mEntryStyles);
622 for (i=0; i<mHeader->styleCount; i++) {
623 e[i] = dtohl(mEntryStyles[i]);
624 }
625 uint32_t* s = const_cast<uint32_t*>(mStyles);
626 for (i=0; i<mStylePoolSize; i++) {
627 s[i] = dtohl(mStyles[i]);
628 }
629 }
630
631 const ResStringPool_span endSpan = {
632 { htodl(ResStringPool_span::END) },
633 htodl(ResStringPool_span::END), htodl(ResStringPool_span::END)
634 };
635 if (memcmp(&mStyles[mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t))],
636 &endSpan, sizeof(endSpan)) != 0) {
637 ALOGW("Bad string block: last style is not 0xFFFFFFFF-terminated\n");
638 return (mError=BAD_TYPE);
639 }
640 } else {
641 mEntryStyles = NULL;
642 mStyles = NULL;
643 mStylePoolSize = 0;
644 }
645
646 return (mError=NO_ERROR);
647 }
648
getError() const649 status_t ResStringPool::getError() const
650 {
651 return mError;
652 }
653
uninit()654 void ResStringPool::uninit()
655 {
656 mError = NO_INIT;
657 if (mHeader != NULL && mCache != NULL) {
658 for (size_t x = 0; x < mHeader->stringCount; x++) {
659 if (mCache[x] != NULL) {
660 free(mCache[x]);
661 mCache[x] = NULL;
662 }
663 }
664 free(mCache);
665 mCache = NULL;
666 }
667 if (mOwnedData) {
668 free(mOwnedData);
669 mOwnedData = NULL;
670 }
671 }
672
673 /**
674 * Strings in UTF-16 format have length indicated by a length encoded in the
675 * stored data. It is either 1 or 2 characters of length data. This allows a
676 * maximum length of 0x7FFFFFF (2147483647 bytes), but if you're storing that
677 * much data in a string, you're abusing them.
678 *
679 * If the high bit is set, then there are two characters or 4 bytes of length
680 * data encoded. In that case, drop the high bit of the first character and
681 * add it together with the next character.
682 */
683 static inline size_t
decodeLength(const uint16_t ** str)684 decodeLength(const uint16_t** str)
685 {
686 size_t len = **str;
687 if ((len & 0x8000) != 0) {
688 (*str)++;
689 len = ((len & 0x7FFF) << 16) | **str;
690 }
691 (*str)++;
692 return len;
693 }
694
695 /**
696 * Strings in UTF-8 format have length indicated by a length encoded in the
697 * stored data. It is either 1 or 2 characters of length data. This allows a
698 * maximum length of 0x7FFF (32767 bytes), but you should consider storing
699 * text in another way if you're using that much data in a single string.
700 *
701 * If the high bit is set, then there are two characters or 2 bytes of length
702 * data encoded. In that case, drop the high bit of the first character and
703 * add it together with the next character.
704 */
705 static inline size_t
decodeLength(const uint8_t ** str)706 decodeLength(const uint8_t** str)
707 {
708 size_t len = **str;
709 if ((len & 0x80) != 0) {
710 (*str)++;
711 len = ((len & 0x7F) << 8) | **str;
712 }
713 (*str)++;
714 return len;
715 }
716
stringAt(size_t idx,size_t * u16len) const717 const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
718 {
719 if (mError == NO_ERROR && idx < mHeader->stringCount) {
720 const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0;
721 const uint32_t off = mEntries[idx]/(isUTF8?sizeof(uint8_t):sizeof(uint16_t));
722 if (off < (mStringPoolSize-1)) {
723 if (!isUTF8) {
724 const uint16_t* strings = (uint16_t*)mStrings;
725 const uint16_t* str = strings+off;
726
727 *u16len = decodeLength(&str);
728 if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) {
729 // Reject malformed (non null-terminated) strings
730 if (str[*u16len] != 0x0000) {
731 ALOGW("Bad string block: string #%d is not null-terminated",
732 (int)idx);
733 return NULL;
734 }
735 return reinterpret_cast<const char16_t*>(str);
736 } else {
737 ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
738 (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize);
739 }
740 } else {
741 const uint8_t* strings = (uint8_t*)mStrings;
742 const uint8_t* u8str = strings+off;
743
744 *u16len = decodeLength(&u8str);
745 size_t u8len = decodeLength(&u8str);
746
747 // encLen must be less than 0x7FFF due to encoding.
748 if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) {
749 AutoMutex lock(mDecodeLock);
750
751 if (mCache != NULL && mCache[idx] != NULL) {
752 return mCache[idx];
753 }
754
755 // Retrieve the actual length of the utf8 string if the
756 // encoded length was truncated
757 if (stringDecodeAt(idx, u8str, u8len, &u8len) == NULL) {
758 return NULL;
759 }
760
761 // Since AAPT truncated lengths longer than 0x7FFF, check
762 // that the bits that remain after truncation at least match
763 // the bits of the actual length
764 ssize_t actualLen = utf8_to_utf16_length(u8str, u8len);
765 if (actualLen < 0 || ((size_t)actualLen & 0x7FFF) != *u16len) {
766 ALOGW("Bad string block: string #%lld decoded length is not correct "
767 "%lld vs %llu\n",
768 (long long)idx, (long long)actualLen, (long long)*u16len);
769 return NULL;
770 }
771
772 *u16len = (size_t) actualLen;
773 char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t));
774 if (!u16str) {
775 ALOGW("No memory when trying to allocate decode cache for string #%d\n",
776 (int)idx);
777 return NULL;
778 }
779
780 utf8_to_utf16(u8str, u8len, u16str, *u16len + 1);
781
782 if (mCache == NULL) {
783 #ifndef __ANDROID__
784 if (kDebugStringPoolNoisy) {
785 ALOGI("CREATING STRING CACHE OF %zu bytes",
786 mHeader->stringCount*sizeof(char16_t**));
787 }
788 #else
789 // We do not want to be in this case when actually running Android.
790 ALOGW("CREATING STRING CACHE OF %zu bytes",
791 static_cast<size_t>(mHeader->stringCount*sizeof(char16_t**)));
792 #endif
793 mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t*));
794 if (mCache == NULL) {
795 ALOGW("No memory trying to allocate decode cache table of %d bytes\n",
796 (int)(mHeader->stringCount*sizeof(char16_t**)));
797 return NULL;
798 }
799 }
800
801 if (kDebugStringPoolNoisy) {
802 ALOGI("Caching UTF8 string: %s", u8str);
803 }
804
805 mCache[idx] = u16str;
806 return u16str;
807 } else {
808 ALOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n",
809 (long long)idx, (long long)(u8str+u8len-strings),
810 (long long)mStringPoolSize);
811 }
812 }
813 } else {
814 ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n",
815 (int)idx, (int)(off*sizeof(uint16_t)),
816 (int)(mStringPoolSize*sizeof(uint16_t)));
817 }
818 }
819 return NULL;
820 }
821
string8At(size_t idx,size_t * outLen) const822 const char* ResStringPool::string8At(size_t idx, size_t* outLen) const
823 {
824 if (mError == NO_ERROR && idx < mHeader->stringCount) {
825 if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) == 0) {
826 return NULL;
827 }
828 const uint32_t off = mEntries[idx]/sizeof(char);
829 if (off < (mStringPoolSize-1)) {
830 const uint8_t* strings = (uint8_t*)mStrings;
831 const uint8_t* str = strings+off;
832
833 // Decode the UTF-16 length. This is not used if we're not
834 // converting to UTF-16 from UTF-8.
835 decodeLength(&str);
836
837 const size_t encLen = decodeLength(&str);
838 *outLen = encLen;
839
840 if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
841 return stringDecodeAt(idx, str, encLen, outLen);
842
843 } else {
844 ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
845 (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
846 }
847 } else {
848 ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n",
849 (int)idx, (int)(off*sizeof(uint16_t)),
850 (int)(mStringPoolSize*sizeof(uint16_t)));
851 }
852 }
853 return NULL;
854 }
855
856 /**
857 * AAPT incorrectly writes a truncated string length when the string size
858 * exceeded the maximum possible encode length value (0x7FFF). To decode a
859 * truncated length, iterate through length values that end in the encode length
860 * bits. Strings that exceed the maximum encode length are not placed into
861 * StringPools in AAPT2.
862 **/
stringDecodeAt(size_t idx,const uint8_t * str,const size_t encLen,size_t * outLen) const863 const char* ResStringPool::stringDecodeAt(size_t idx, const uint8_t* str,
864 const size_t encLen, size_t* outLen) const {
865 const uint8_t* strings = (uint8_t*)mStrings;
866
867 size_t i = 0, end = encLen;
868 while ((uint32_t)(str+end-strings) < mStringPoolSize) {
869 if (str[end] == 0x00) {
870 if (i != 0) {
871 ALOGW("Bad string block: string #%d is truncated (actual length is %d)",
872 (int)idx, (int)end);
873 }
874
875 *outLen = end;
876 return (const char*)str;
877 }
878
879 end = (++i << (sizeof(uint8_t) * 8 * 2 - 1)) | encLen;
880 }
881
882 // Reject malformed (non null-terminated) strings
883 ALOGW("Bad string block: string #%d is not null-terminated",
884 (int)idx);
885 return NULL;
886 }
887
string8ObjectAt(size_t idx) const888 const String8 ResStringPool::string8ObjectAt(size_t idx) const
889 {
890 size_t len;
891 const char *str = string8At(idx, &len);
892 if (str != NULL) {
893 return String8(str, len);
894 }
895
896 const char16_t *str16 = stringAt(idx, &len);
897 if (str16 != NULL) {
898 return String8(str16, len);
899 }
900 return String8();
901 }
902
styleAt(const ResStringPool_ref & ref) const903 const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const
904 {
905 return styleAt(ref.index);
906 }
907
styleAt(size_t idx) const908 const ResStringPool_span* ResStringPool::styleAt(size_t idx) const
909 {
910 if (mError == NO_ERROR && idx < mHeader->styleCount) {
911 const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t));
912 if (off < mStylePoolSize) {
913 return (const ResStringPool_span*)(mStyles+off);
914 } else {
915 ALOGW("Bad string block: style #%d entry is at %d, past end at %d\n",
916 (int)idx, (int)(off*sizeof(uint32_t)),
917 (int)(mStylePoolSize*sizeof(uint32_t)));
918 }
919 }
920 return NULL;
921 }
922
indexOfString(const char16_t * str,size_t strLen) const923 ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
924 {
925 if (mError != NO_ERROR) {
926 return mError;
927 }
928
929 size_t len;
930
931 if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) {
932 if (kDebugStringPoolNoisy) {
933 ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string());
934 }
935
936 // The string pool contains UTF 8 strings; we don't want to cause
937 // temporary UTF-16 strings to be created as we search.
938 if (mHeader->flags&ResStringPool_header::SORTED_FLAG) {
939 // Do a binary search for the string... this is a little tricky,
940 // because the strings are sorted with strzcmp16(). So to match
941 // the ordering, we need to convert strings in the pool to UTF-16.
942 // But we don't want to hit the cache, so instead we will have a
943 // local temporary allocation for the conversions.
944 size_t convBufferLen = strLen + 4;
945 char16_t* convBuffer = (char16_t*)calloc(convBufferLen, sizeof(char16_t));
946 ssize_t l = 0;
947 ssize_t h = mHeader->stringCount-1;
948
949 ssize_t mid;
950 while (l <= h) {
951 mid = l + (h - l)/2;
952 const uint8_t* s = (const uint8_t*)string8At(mid, &len);
953 int c;
954 if (s != NULL) {
955 char16_t* end = utf8_to_utf16(s, len, convBuffer, convBufferLen);
956 c = strzcmp16(convBuffer, end-convBuffer, str, strLen);
957 } else {
958 c = -1;
959 }
960 if (kDebugStringPoolNoisy) {
961 ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
962 (const char*)s, c, (int)l, (int)mid, (int)h);
963 }
964 if (c == 0) {
965 if (kDebugStringPoolNoisy) {
966 ALOGI("MATCH!");
967 }
968 free(convBuffer);
969 return mid;
970 } else if (c < 0) {
971 l = mid + 1;
972 } else {
973 h = mid - 1;
974 }
975 }
976 free(convBuffer);
977 } else {
978 // It is unusual to get the ID from an unsorted string block...
979 // most often this happens because we want to get IDs for style
980 // span tags; since those always appear at the end of the string
981 // block, start searching at the back.
982 String8 str8(str, strLen);
983 const size_t str8Len = str8.size();
984 for (int i=mHeader->stringCount-1; i>=0; i--) {
985 const char* s = string8At(i, &len);
986 if (kDebugStringPoolNoisy) {
987 ALOGI("Looking at %s, i=%d\n", String8(s).string(), i);
988 }
989 if (s && str8Len == len && memcmp(s, str8.string(), str8Len) == 0) {
990 if (kDebugStringPoolNoisy) {
991 ALOGI("MATCH!");
992 }
993 return i;
994 }
995 }
996 }
997
998 } else {
999 if (kDebugStringPoolNoisy) {
1000 ALOGI("indexOfString UTF-16: %s", String8(str, strLen).string());
1001 }
1002
1003 if (mHeader->flags&ResStringPool_header::SORTED_FLAG) {
1004 // Do a binary search for the string...
1005 ssize_t l = 0;
1006 ssize_t h = mHeader->stringCount-1;
1007
1008 ssize_t mid;
1009 while (l <= h) {
1010 mid = l + (h - l)/2;
1011 const char16_t* s = stringAt(mid, &len);
1012 int c = s ? strzcmp16(s, len, str, strLen) : -1;
1013 if (kDebugStringPoolNoisy) {
1014 ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
1015 String8(s).string(), c, (int)l, (int)mid, (int)h);
1016 }
1017 if (c == 0) {
1018 if (kDebugStringPoolNoisy) {
1019 ALOGI("MATCH!");
1020 }
1021 return mid;
1022 } else if (c < 0) {
1023 l = mid + 1;
1024 } else {
1025 h = mid - 1;
1026 }
1027 }
1028 } else {
1029 // It is unusual to get the ID from an unsorted string block...
1030 // most often this happens because we want to get IDs for style
1031 // span tags; since those always appear at the end of the string
1032 // block, start searching at the back.
1033 for (int i=mHeader->stringCount-1; i>=0; i--) {
1034 const char16_t* s = stringAt(i, &len);
1035 if (kDebugStringPoolNoisy) {
1036 ALOGI("Looking at %s, i=%d\n", String8(s).string(), i);
1037 }
1038 if (s && strLen == len && strzcmp16(s, len, str, strLen) == 0) {
1039 if (kDebugStringPoolNoisy) {
1040 ALOGI("MATCH!");
1041 }
1042 return i;
1043 }
1044 }
1045 }
1046 }
1047
1048 return NAME_NOT_FOUND;
1049 }
1050
size() const1051 size_t ResStringPool::size() const
1052 {
1053 return (mError == NO_ERROR) ? mHeader->stringCount : 0;
1054 }
1055
styleCount() const1056 size_t ResStringPool::styleCount() const
1057 {
1058 return (mError == NO_ERROR) ? mHeader->styleCount : 0;
1059 }
1060
bytes() const1061 size_t ResStringPool::bytes() const
1062 {
1063 return (mError == NO_ERROR) ? mHeader->header.size : 0;
1064 }
1065
isSorted() const1066 bool ResStringPool::isSorted() const
1067 {
1068 return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0;
1069 }
1070
isUTF8() const1071 bool ResStringPool::isUTF8() const
1072 {
1073 return (mHeader->flags&ResStringPool_header::UTF8_FLAG)!=0;
1074 }
1075
1076 // --------------------------------------------------------------------
1077 // --------------------------------------------------------------------
1078 // --------------------------------------------------------------------
1079
ResXMLParser(const ResXMLTree & tree)1080 ResXMLParser::ResXMLParser(const ResXMLTree& tree)
1081 : mTree(tree), mEventCode(BAD_DOCUMENT)
1082 {
1083 }
1084
restart()1085 void ResXMLParser::restart()
1086 {
1087 mCurNode = NULL;
1088 mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT;
1089 }
getStrings() const1090 const ResStringPool& ResXMLParser::getStrings() const
1091 {
1092 return mTree.mStrings;
1093 }
1094
getEventType() const1095 ResXMLParser::event_code_t ResXMLParser::getEventType() const
1096 {
1097 return mEventCode;
1098 }
1099
next()1100 ResXMLParser::event_code_t ResXMLParser::next()
1101 {
1102 if (mEventCode == START_DOCUMENT) {
1103 mCurNode = mTree.mRootNode;
1104 mCurExt = mTree.mRootExt;
1105 return (mEventCode=mTree.mRootCode);
1106 } else if (mEventCode >= FIRST_CHUNK_CODE) {
1107 return nextNode();
1108 }
1109 return mEventCode;
1110 }
1111
getCommentID() const1112 int32_t ResXMLParser::getCommentID() const
1113 {
1114 return mCurNode != NULL ? dtohl(mCurNode->comment.index) : -1;
1115 }
1116
getComment(size_t * outLen) const1117 const char16_t* ResXMLParser::getComment(size_t* outLen) const
1118 {
1119 int32_t id = getCommentID();
1120 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1121 }
1122
getLineNumber() const1123 uint32_t ResXMLParser::getLineNumber() const
1124 {
1125 return mCurNode != NULL ? dtohl(mCurNode->lineNumber) : -1;
1126 }
1127
getTextID() const1128 int32_t ResXMLParser::getTextID() const
1129 {
1130 if (mEventCode == TEXT) {
1131 return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index);
1132 }
1133 return -1;
1134 }
1135
getText(size_t * outLen) const1136 const char16_t* ResXMLParser::getText(size_t* outLen) const
1137 {
1138 int32_t id = getTextID();
1139 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1140 }
1141
getTextValue(Res_value * outValue) const1142 ssize_t ResXMLParser::getTextValue(Res_value* outValue) const
1143 {
1144 if (mEventCode == TEXT) {
1145 outValue->copyFrom_dtoh(((const ResXMLTree_cdataExt*)mCurExt)->typedData);
1146 return sizeof(Res_value);
1147 }
1148 return BAD_TYPE;
1149 }
1150
getNamespacePrefixID() const1151 int32_t ResXMLParser::getNamespacePrefixID() const
1152 {
1153 if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) {
1154 return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->prefix.index);
1155 }
1156 return -1;
1157 }
1158
getNamespacePrefix(size_t * outLen) const1159 const char16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const
1160 {
1161 int32_t id = getNamespacePrefixID();
1162 //printf("prefix=%d event=%p\n", id, mEventCode);
1163 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1164 }
1165
getNamespaceUriID() const1166 int32_t ResXMLParser::getNamespaceUriID() const
1167 {
1168 if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) {
1169 return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->uri.index);
1170 }
1171 return -1;
1172 }
1173
getNamespaceUri(size_t * outLen) const1174 const char16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const
1175 {
1176 int32_t id = getNamespaceUriID();
1177 //printf("uri=%d event=%p\n", id, mEventCode);
1178 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1179 }
1180
getElementNamespaceID() const1181 int32_t ResXMLParser::getElementNamespaceID() const
1182 {
1183 if (mEventCode == START_TAG) {
1184 return dtohl(((const ResXMLTree_attrExt*)mCurExt)->ns.index);
1185 }
1186 if (mEventCode == END_TAG) {
1187 return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->ns.index);
1188 }
1189 return -1;
1190 }
1191
getElementNamespace(size_t * outLen) const1192 const char16_t* ResXMLParser::getElementNamespace(size_t* outLen) const
1193 {
1194 int32_t id = getElementNamespaceID();
1195 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1196 }
1197
getElementNameID() const1198 int32_t ResXMLParser::getElementNameID() const
1199 {
1200 if (mEventCode == START_TAG) {
1201 return dtohl(((const ResXMLTree_attrExt*)mCurExt)->name.index);
1202 }
1203 if (mEventCode == END_TAG) {
1204 return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->name.index);
1205 }
1206 return -1;
1207 }
1208
getElementName(size_t * outLen) const1209 const char16_t* ResXMLParser::getElementName(size_t* outLen) const
1210 {
1211 int32_t id = getElementNameID();
1212 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1213 }
1214
getAttributeCount() const1215 size_t ResXMLParser::getAttributeCount() const
1216 {
1217 if (mEventCode == START_TAG) {
1218 return dtohs(((const ResXMLTree_attrExt*)mCurExt)->attributeCount);
1219 }
1220 return 0;
1221 }
1222
getAttributeNamespaceID(size_t idx) const1223 int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const
1224 {
1225 if (mEventCode == START_TAG) {
1226 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
1227 if (idx < dtohs(tag->attributeCount)) {
1228 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
1229 (((const uint8_t*)tag)
1230 + dtohs(tag->attributeStart)
1231 + (dtohs(tag->attributeSize)*idx));
1232 return dtohl(attr->ns.index);
1233 }
1234 }
1235 return -2;
1236 }
1237
getAttributeNamespace(size_t idx,size_t * outLen) const1238 const char16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) const
1239 {
1240 int32_t id = getAttributeNamespaceID(idx);
1241 //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode);
1242 if (kDebugXMLNoisy) {
1243 printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id);
1244 }
1245 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1246 }
1247
getAttributeNamespace8(size_t idx,size_t * outLen) const1248 const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) const
1249 {
1250 int32_t id = getAttributeNamespaceID(idx);
1251 //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode);
1252 if (kDebugXMLNoisy) {
1253 printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id);
1254 }
1255 return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL;
1256 }
1257
getAttributeNameID(size_t idx) const1258 int32_t ResXMLParser::getAttributeNameID(size_t idx) const
1259 {
1260 if (mEventCode == START_TAG) {
1261 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
1262 if (idx < dtohs(tag->attributeCount)) {
1263 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
1264 (((const uint8_t*)tag)
1265 + dtohs(tag->attributeStart)
1266 + (dtohs(tag->attributeSize)*idx));
1267 return dtohl(attr->name.index);
1268 }
1269 }
1270 return -1;
1271 }
1272
getAttributeName(size_t idx,size_t * outLen) const1273 const char16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const
1274 {
1275 int32_t id = getAttributeNameID(idx);
1276 //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode);
1277 if (kDebugXMLNoisy) {
1278 printf("getAttributeName 0x%zx=0x%x\n", idx, id);
1279 }
1280 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1281 }
1282
getAttributeName8(size_t idx,size_t * outLen) const1283 const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const
1284 {
1285 int32_t id = getAttributeNameID(idx);
1286 //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode);
1287 if (kDebugXMLNoisy) {
1288 printf("getAttributeName 0x%zx=0x%x\n", idx, id);
1289 }
1290 return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL;
1291 }
1292
getAttributeNameResID(size_t idx) const1293 uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const
1294 {
1295 int32_t id = getAttributeNameID(idx);
1296 if (id >= 0 && (size_t)id < mTree.mNumResIds) {
1297 uint32_t resId = dtohl(mTree.mResIds[id]);
1298 if (mTree.mDynamicRefTable != NULL) {
1299 mTree.mDynamicRefTable->lookupResourceId(&resId);
1300 }
1301 return resId;
1302 }
1303 return 0;
1304 }
1305
getAttributeValueStringID(size_t idx) const1306 int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const
1307 {
1308 if (mEventCode == START_TAG) {
1309 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
1310 if (idx < dtohs(tag->attributeCount)) {
1311 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
1312 (((const uint8_t*)tag)
1313 + dtohs(tag->attributeStart)
1314 + (dtohs(tag->attributeSize)*idx));
1315 return dtohl(attr->rawValue.index);
1316 }
1317 }
1318 return -1;
1319 }
1320
getAttributeStringValue(size_t idx,size_t * outLen) const1321 const char16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen) const
1322 {
1323 int32_t id = getAttributeValueStringID(idx);
1324 if (kDebugXMLNoisy) {
1325 printf("getAttributeValue 0x%zx=0x%x\n", idx, id);
1326 }
1327 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1328 }
1329
getAttributeDataType(size_t idx) const1330 int32_t ResXMLParser::getAttributeDataType(size_t idx) const
1331 {
1332 if (mEventCode == START_TAG) {
1333 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
1334 if (idx < dtohs(tag->attributeCount)) {
1335 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
1336 (((const uint8_t*)tag)
1337 + dtohs(tag->attributeStart)
1338 + (dtohs(tag->attributeSize)*idx));
1339 uint8_t type = attr->typedValue.dataType;
1340 if (type != Res_value::TYPE_DYNAMIC_REFERENCE) {
1341 return type;
1342 }
1343
1344 // This is a dynamic reference. We adjust those references
1345 // to regular references at this level, so lie to the caller.
1346 return Res_value::TYPE_REFERENCE;
1347 }
1348 }
1349 return Res_value::TYPE_NULL;
1350 }
1351
getAttributeData(size_t idx) const1352 int32_t ResXMLParser::getAttributeData(size_t idx) const
1353 {
1354 if (mEventCode == START_TAG) {
1355 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
1356 if (idx < dtohs(tag->attributeCount)) {
1357 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
1358 (((const uint8_t*)tag)
1359 + dtohs(tag->attributeStart)
1360 + (dtohs(tag->attributeSize)*idx));
1361 if (attr->typedValue.dataType != Res_value::TYPE_DYNAMIC_REFERENCE ||
1362 mTree.mDynamicRefTable == NULL) {
1363 return dtohl(attr->typedValue.data);
1364 }
1365
1366 uint32_t data = dtohl(attr->typedValue.data);
1367 if (mTree.mDynamicRefTable->lookupResourceId(&data) == NO_ERROR) {
1368 return data;
1369 }
1370 }
1371 }
1372 return 0;
1373 }
1374
getAttributeValue(size_t idx,Res_value * outValue) const1375 ssize_t ResXMLParser::getAttributeValue(size_t idx, Res_value* outValue) const
1376 {
1377 if (mEventCode == START_TAG) {
1378 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
1379 if (idx < dtohs(tag->attributeCount)) {
1380 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
1381 (((const uint8_t*)tag)
1382 + dtohs(tag->attributeStart)
1383 + (dtohs(tag->attributeSize)*idx));
1384 outValue->copyFrom_dtoh(attr->typedValue);
1385 if (mTree.mDynamicRefTable != NULL &&
1386 mTree.mDynamicRefTable->lookupResourceValue(outValue) != NO_ERROR) {
1387 return BAD_TYPE;
1388 }
1389 return sizeof(Res_value);
1390 }
1391 }
1392 return BAD_TYPE;
1393 }
1394
indexOfAttribute(const char * ns,const char * attr) const1395 ssize_t ResXMLParser::indexOfAttribute(const char* ns, const char* attr) const
1396 {
1397 String16 nsStr(ns != NULL ? ns : "");
1398 String16 attrStr(attr);
1399 return indexOfAttribute(ns ? nsStr.string() : NULL, ns ? nsStr.size() : 0,
1400 attrStr.string(), attrStr.size());
1401 }
1402
indexOfAttribute(const char16_t * ns,size_t nsLen,const char16_t * attr,size_t attrLen) const1403 ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen,
1404 const char16_t* attr, size_t attrLen) const
1405 {
1406 if (mEventCode == START_TAG) {
1407 if (attr == NULL) {
1408 return NAME_NOT_FOUND;
1409 }
1410 const size_t N = getAttributeCount();
1411 if (mTree.mStrings.isUTF8()) {
1412 String8 ns8, attr8;
1413 if (ns != NULL) {
1414 ns8 = String8(ns, nsLen);
1415 }
1416 attr8 = String8(attr, attrLen);
1417 if (kDebugStringPoolNoisy) {
1418 ALOGI("indexOfAttribute UTF8 %s (%zu) / %s (%zu)", ns8.string(), nsLen,
1419 attr8.string(), attrLen);
1420 }
1421 for (size_t i=0; i<N; i++) {
1422 size_t curNsLen = 0, curAttrLen = 0;
1423 const char* curNs = getAttributeNamespace8(i, &curNsLen);
1424 const char* curAttr = getAttributeName8(i, &curAttrLen);
1425 if (kDebugStringPoolNoisy) {
1426 ALOGI(" curNs=%s (%zu), curAttr=%s (%zu)", curNs, curNsLen, curAttr, curAttrLen);
1427 }
1428 if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen
1429 && memcmp(attr8.string(), curAttr, attrLen) == 0) {
1430 if (ns == NULL) {
1431 if (curNs == NULL) {
1432 if (kDebugStringPoolNoisy) {
1433 ALOGI(" FOUND!");
1434 }
1435 return i;
1436 }
1437 } else if (curNs != NULL) {
1438 //printf(" --> ns=%s, curNs=%s\n",
1439 // String8(ns).string(), String8(curNs).string());
1440 if (memcmp(ns8.string(), curNs, nsLen) == 0) {
1441 if (kDebugStringPoolNoisy) {
1442 ALOGI(" FOUND!");
1443 }
1444 return i;
1445 }
1446 }
1447 }
1448 }
1449 } else {
1450 if (kDebugStringPoolNoisy) {
1451 ALOGI("indexOfAttribute UTF16 %s (%zu) / %s (%zu)",
1452 String8(ns, nsLen).string(), nsLen,
1453 String8(attr, attrLen).string(), attrLen);
1454 }
1455 for (size_t i=0; i<N; i++) {
1456 size_t curNsLen = 0, curAttrLen = 0;
1457 const char16_t* curNs = getAttributeNamespace(i, &curNsLen);
1458 const char16_t* curAttr = getAttributeName(i, &curAttrLen);
1459 if (kDebugStringPoolNoisy) {
1460 ALOGI(" curNs=%s (%zu), curAttr=%s (%zu)",
1461 String8(curNs, curNsLen).string(), curNsLen,
1462 String8(curAttr, curAttrLen).string(), curAttrLen);
1463 }
1464 if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen
1465 && (memcmp(attr, curAttr, attrLen*sizeof(char16_t)) == 0)) {
1466 if (ns == NULL) {
1467 if (curNs == NULL) {
1468 if (kDebugStringPoolNoisy) {
1469 ALOGI(" FOUND!");
1470 }
1471 return i;
1472 }
1473 } else if (curNs != NULL) {
1474 //printf(" --> ns=%s, curNs=%s\n",
1475 // String8(ns).string(), String8(curNs).string());
1476 if (memcmp(ns, curNs, nsLen*sizeof(char16_t)) == 0) {
1477 if (kDebugStringPoolNoisy) {
1478 ALOGI(" FOUND!");
1479 }
1480 return i;
1481 }
1482 }
1483 }
1484 }
1485 }
1486 }
1487
1488 return NAME_NOT_FOUND;
1489 }
1490
indexOfID() const1491 ssize_t ResXMLParser::indexOfID() const
1492 {
1493 if (mEventCode == START_TAG) {
1494 const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->idIndex);
1495 if (idx > 0) return (idx-1);
1496 }
1497 return NAME_NOT_FOUND;
1498 }
1499
indexOfClass() const1500 ssize_t ResXMLParser::indexOfClass() const
1501 {
1502 if (mEventCode == START_TAG) {
1503 const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->classIndex);
1504 if (idx > 0) return (idx-1);
1505 }
1506 return NAME_NOT_FOUND;
1507 }
1508
indexOfStyle() const1509 ssize_t ResXMLParser::indexOfStyle() const
1510 {
1511 if (mEventCode == START_TAG) {
1512 const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->styleIndex);
1513 if (idx > 0) return (idx-1);
1514 }
1515 return NAME_NOT_FOUND;
1516 }
1517
nextNode()1518 ResXMLParser::event_code_t ResXMLParser::nextNode()
1519 {
1520 if (mEventCode < 0) {
1521 return mEventCode;
1522 }
1523
1524 do {
1525 const ResXMLTree_node* next = (const ResXMLTree_node*)
1526 (((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size));
1527 if (kDebugXMLNoisy) {
1528 ALOGI("Next node: prev=%p, next=%p\n", mCurNode, next);
1529 }
1530
1531 if (((const uint8_t*)next) >= mTree.mDataEnd) {
1532 mCurNode = NULL;
1533 return (mEventCode=END_DOCUMENT);
1534 }
1535
1536 if (mTree.validateNode(next) != NO_ERROR) {
1537 mCurNode = NULL;
1538 return (mEventCode=BAD_DOCUMENT);
1539 }
1540
1541 mCurNode = next;
1542 const uint16_t headerSize = dtohs(next->header.headerSize);
1543 const uint32_t totalSize = dtohl(next->header.size);
1544 mCurExt = ((const uint8_t*)next) + headerSize;
1545 size_t minExtSize = 0;
1546 event_code_t eventCode = (event_code_t)dtohs(next->header.type);
1547 switch ((mEventCode=eventCode)) {
1548 case RES_XML_START_NAMESPACE_TYPE:
1549 case RES_XML_END_NAMESPACE_TYPE:
1550 minExtSize = sizeof(ResXMLTree_namespaceExt);
1551 break;
1552 case RES_XML_START_ELEMENT_TYPE:
1553 minExtSize = sizeof(ResXMLTree_attrExt);
1554 break;
1555 case RES_XML_END_ELEMENT_TYPE:
1556 minExtSize = sizeof(ResXMLTree_endElementExt);
1557 break;
1558 case RES_XML_CDATA_TYPE:
1559 minExtSize = sizeof(ResXMLTree_cdataExt);
1560 break;
1561 default:
1562 ALOGW("Unknown XML block: header type %d in node at %d\n",
1563 (int)dtohs(next->header.type),
1564 (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)));
1565 continue;
1566 }
1567
1568 if ((totalSize-headerSize) < minExtSize) {
1569 ALOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n",
1570 (int)dtohs(next->header.type),
1571 (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)),
1572 (int)(totalSize-headerSize), (int)minExtSize);
1573 return (mEventCode=BAD_DOCUMENT);
1574 }
1575
1576 //printf("CurNode=%p, CurExt=%p, headerSize=%d, minExtSize=%d\n",
1577 // mCurNode, mCurExt, headerSize, minExtSize);
1578
1579 return eventCode;
1580 } while (true);
1581 }
1582
getPosition(ResXMLParser::ResXMLPosition * pos) const1583 void ResXMLParser::getPosition(ResXMLParser::ResXMLPosition* pos) const
1584 {
1585 pos->eventCode = mEventCode;
1586 pos->curNode = mCurNode;
1587 pos->curExt = mCurExt;
1588 }
1589
setPosition(const ResXMLParser::ResXMLPosition & pos)1590 void ResXMLParser::setPosition(const ResXMLParser::ResXMLPosition& pos)
1591 {
1592 mEventCode = pos.eventCode;
1593 mCurNode = pos.curNode;
1594 mCurExt = pos.curExt;
1595 }
1596
setSourceResourceId(const uint32_t resId)1597 void ResXMLParser::setSourceResourceId(const uint32_t resId)
1598 {
1599 mSourceResourceId = resId;
1600 }
1601
getSourceResourceId() const1602 uint32_t ResXMLParser::getSourceResourceId() const
1603 {
1604 return mSourceResourceId;
1605 }
1606
1607 // --------------------------------------------------------------------
1608
1609 static volatile int32_t gCount = 0;
1610
ResXMLTree(const DynamicRefTable * dynamicRefTable)1611 ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable)
1612 : ResXMLParser(*this)
1613 , mDynamicRefTable((dynamicRefTable != nullptr) ? dynamicRefTable->clone()
1614 : std::unique_ptr<DynamicRefTable>(nullptr))
1615 , mError(NO_INIT), mOwnedData(NULL)
1616 {
1617 if (kDebugResXMLTree) {
1618 ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1);
1619 }
1620 restart();
1621 }
1622
ResXMLTree()1623 ResXMLTree::ResXMLTree()
1624 : ResXMLParser(*this)
1625 , mDynamicRefTable(std::unique_ptr<DynamicRefTable>(nullptr))
1626 , mError(NO_INIT), mOwnedData(NULL)
1627 {
1628 if (kDebugResXMLTree) {
1629 ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1);
1630 }
1631 restart();
1632 }
1633
~ResXMLTree()1634 ResXMLTree::~ResXMLTree()
1635 {
1636 if (kDebugResXMLTree) {
1637 ALOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1);
1638 }
1639 uninit();
1640 }
1641
setTo(const void * data,size_t size,bool copyData)1642 status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData)
1643 {
1644 uninit();
1645 mEventCode = START_DOCUMENT;
1646
1647 if (!data || !size) {
1648 return (mError=BAD_TYPE);
1649 }
1650
1651 if (copyData) {
1652 mOwnedData = malloc(size);
1653 if (mOwnedData == NULL) {
1654 return (mError=NO_MEMORY);
1655 }
1656 memcpy(mOwnedData, data, size);
1657 data = mOwnedData;
1658 }
1659
1660 mHeader = (const ResXMLTree_header*)data;
1661 mSize = dtohl(mHeader->header.size);
1662 if (dtohs(mHeader->header.headerSize) > mSize || mSize > size) {
1663 ALOGW("Bad XML block: header size %d or total size %d is larger than data size %d\n",
1664 (int)dtohs(mHeader->header.headerSize),
1665 (int)dtohl(mHeader->header.size), (int)size);
1666 mError = BAD_TYPE;
1667 restart();
1668 return mError;
1669 }
1670 mDataEnd = ((const uint8_t*)mHeader) + mSize;
1671
1672 mStrings.uninit();
1673 mRootNode = NULL;
1674 mResIds = NULL;
1675 mNumResIds = 0;
1676
1677 // First look for a couple interesting chunks: the string block
1678 // and first XML node.
1679 const ResChunk_header* chunk =
1680 (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize));
1681 const ResChunk_header* lastChunk = chunk;
1682 while (((const uint8_t*)chunk) < (mDataEnd-sizeof(ResChunk_header)) &&
1683 ((const uint8_t*)chunk) < (mDataEnd-dtohl(chunk->size))) {
1684 status_t err = validate_chunk(chunk, sizeof(ResChunk_header), mDataEnd, "XML");
1685 if (err != NO_ERROR) {
1686 mError = err;
1687 goto done;
1688 }
1689 const uint16_t type = dtohs(chunk->type);
1690 const size_t size = dtohl(chunk->size);
1691 if (kDebugXMLNoisy) {
1692 printf("Scanning @ %p: type=0x%x, size=0x%zx\n",
1693 (void*)(((uintptr_t)chunk)-((uintptr_t)mHeader)), type, size);
1694 }
1695 if (type == RES_STRING_POOL_TYPE) {
1696 mStrings.setTo(chunk, size);
1697 } else if (type == RES_XML_RESOURCE_MAP_TYPE) {
1698 mResIds = (const uint32_t*)
1699 (((const uint8_t*)chunk)+dtohs(chunk->headerSize));
1700 mNumResIds = (dtohl(chunk->size)-dtohs(chunk->headerSize))/sizeof(uint32_t);
1701 } else if (type >= RES_XML_FIRST_CHUNK_TYPE
1702 && type <= RES_XML_LAST_CHUNK_TYPE) {
1703 if (validateNode((const ResXMLTree_node*)chunk) != NO_ERROR) {
1704 mError = BAD_TYPE;
1705 goto done;
1706 }
1707 mCurNode = (const ResXMLTree_node*)lastChunk;
1708 if (nextNode() == BAD_DOCUMENT) {
1709 mError = BAD_TYPE;
1710 goto done;
1711 }
1712 mRootNode = mCurNode;
1713 mRootExt = mCurExt;
1714 mRootCode = mEventCode;
1715 break;
1716 } else {
1717 if (kDebugXMLNoisy) {
1718 printf("Skipping unknown chunk!\n");
1719 }
1720 }
1721 lastChunk = chunk;
1722 chunk = (const ResChunk_header*)
1723 (((const uint8_t*)chunk) + size);
1724 }
1725
1726 if (mRootNode == NULL) {
1727 ALOGW("Bad XML block: no root element node found\n");
1728 mError = BAD_TYPE;
1729 goto done;
1730 }
1731
1732 mError = mStrings.getError();
1733
1734 done:
1735 restart();
1736 return mError;
1737 }
1738
getError() const1739 status_t ResXMLTree::getError() const
1740 {
1741 return mError;
1742 }
1743
uninit()1744 void ResXMLTree::uninit()
1745 {
1746 mError = NO_INIT;
1747 mStrings.uninit();
1748 if (mOwnedData) {
1749 free(mOwnedData);
1750 mOwnedData = NULL;
1751 }
1752 restart();
1753 }
1754
validateNode(const ResXMLTree_node * node) const1755 status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const
1756 {
1757 const uint16_t eventCode = dtohs(node->header.type);
1758
1759 status_t err = validate_chunk(
1760 &node->header, sizeof(ResXMLTree_node),
1761 mDataEnd, "ResXMLTree_node");
1762
1763 if (err >= NO_ERROR) {
1764 // Only perform additional validation on START nodes
1765 if (eventCode != RES_XML_START_ELEMENT_TYPE) {
1766 return NO_ERROR;
1767 }
1768
1769 const uint16_t headerSize = dtohs(node->header.headerSize);
1770 const uint32_t size = dtohl(node->header.size);
1771 const ResXMLTree_attrExt* attrExt = (const ResXMLTree_attrExt*)
1772 (((const uint8_t*)node) + headerSize);
1773 // check for sensical values pulled out of the stream so far...
1774 if ((size >= headerSize + sizeof(ResXMLTree_attrExt))
1775 && ((void*)attrExt > (void*)node)) {
1776 const size_t attrSize = ((size_t)dtohs(attrExt->attributeSize))
1777 * dtohs(attrExt->attributeCount);
1778 if ((dtohs(attrExt->attributeStart)+attrSize) <= (size-headerSize)) {
1779 return NO_ERROR;
1780 }
1781 ALOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n",
1782 (unsigned int)(dtohs(attrExt->attributeStart)+attrSize),
1783 (unsigned int)(size-headerSize));
1784 }
1785 else {
1786 ALOGW("Bad XML start block: node header size 0x%x, size 0x%x\n",
1787 (unsigned int)headerSize, (unsigned int)size);
1788 }
1789 return BAD_TYPE;
1790 }
1791
1792 return err;
1793
1794 #if 0
1795 const bool isStart = dtohs(node->header.type) == RES_XML_START_ELEMENT_TYPE;
1796
1797 const uint16_t headerSize = dtohs(node->header.headerSize);
1798 const uint32_t size = dtohl(node->header.size);
1799
1800 if (headerSize >= (isStart ? sizeof(ResXMLTree_attrNode) : sizeof(ResXMLTree_node))) {
1801 if (size >= headerSize) {
1802 if (((const uint8_t*)node) <= (mDataEnd-size)) {
1803 if (!isStart) {
1804 return NO_ERROR;
1805 }
1806 if ((((size_t)dtohs(node->attributeSize))*dtohs(node->attributeCount))
1807 <= (size-headerSize)) {
1808 return NO_ERROR;
1809 }
1810 ALOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n",
1811 ((int)dtohs(node->attributeSize))*dtohs(node->attributeCount),
1812 (int)(size-headerSize));
1813 return BAD_TYPE;
1814 }
1815 ALOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n",
1816 (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), (int)mSize);
1817 return BAD_TYPE;
1818 }
1819 ALOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n",
1820 (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)),
1821 (int)headerSize, (int)size);
1822 return BAD_TYPE;
1823 }
1824 ALOGW("Bad XML block: node at 0x%x header size 0x%x too small\n",
1825 (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)),
1826 (int)headerSize);
1827 return BAD_TYPE;
1828 #endif
1829 }
1830
1831 // --------------------------------------------------------------------
1832 // --------------------------------------------------------------------
1833 // --------------------------------------------------------------------
1834
copyFromDeviceNoSwap(const ResTable_config & o)1835 void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) {
1836 const size_t size = dtohl(o.size);
1837 if (size >= sizeof(ResTable_config)) {
1838 *this = o;
1839 } else {
1840 memcpy(this, &o, size);
1841 memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size);
1842 }
1843 }
1844
unpackLanguageOrRegion(const char in[2],const char base,char out[4])1845 /* static */ size_t unpackLanguageOrRegion(const char in[2], const char base,
1846 char out[4]) {
1847 if (in[0] & 0x80) {
1848 // The high bit is "1", which means this is a packed three letter
1849 // language code.
1850
1851 // The smallest 5 bits of the second char are the first alphabet.
1852 const uint8_t first = in[1] & 0x1f;
1853 // The last three bits of the second char and the first two bits
1854 // of the first char are the second alphabet.
1855 const uint8_t second = ((in[1] & 0xe0) >> 5) + ((in[0] & 0x03) << 3);
1856 // Bits 3 to 7 (inclusive) of the first char are the third alphabet.
1857 const uint8_t third = (in[0] & 0x7c) >> 2;
1858
1859 out[0] = first + base;
1860 out[1] = second + base;
1861 out[2] = third + base;
1862 out[3] = 0;
1863
1864 return 3;
1865 }
1866
1867 if (in[0]) {
1868 memcpy(out, in, 2);
1869 memset(out + 2, 0, 2);
1870 return 2;
1871 }
1872
1873 memset(out, 0, 4);
1874 return 0;
1875 }
1876
packLanguageOrRegion(const char * in,const char base,char out[2])1877 /* static */ void packLanguageOrRegion(const char* in, const char base,
1878 char out[2]) {
1879 if (in[2] == 0 || in[2] == '-') {
1880 out[0] = in[0];
1881 out[1] = in[1];
1882 } else {
1883 uint8_t first = (in[0] - base) & 0x007f;
1884 uint8_t second = (in[1] - base) & 0x007f;
1885 uint8_t third = (in[2] - base) & 0x007f;
1886
1887 out[0] = (0x80 | (third << 2) | (second >> 3));
1888 out[1] = ((second << 5) | first);
1889 }
1890 }
1891
1892
packLanguage(const char * language)1893 void ResTable_config::packLanguage(const char* language) {
1894 packLanguageOrRegion(language, 'a', this->language);
1895 }
1896
packRegion(const char * region)1897 void ResTable_config::packRegion(const char* region) {
1898 packLanguageOrRegion(region, '0', this->country);
1899 }
1900
unpackLanguage(char language[4]) const1901 size_t ResTable_config::unpackLanguage(char language[4]) const {
1902 return unpackLanguageOrRegion(this->language, 'a', language);
1903 }
1904
unpackRegion(char region[4]) const1905 size_t ResTable_config::unpackRegion(char region[4]) const {
1906 return unpackLanguageOrRegion(this->country, '0', region);
1907 }
1908
1909
copyFromDtoH(const ResTable_config & o)1910 void ResTable_config::copyFromDtoH(const ResTable_config& o) {
1911 copyFromDeviceNoSwap(o);
1912 size = sizeof(ResTable_config);
1913 mcc = dtohs(mcc);
1914 mnc = dtohs(mnc);
1915 density = dtohs(density);
1916 screenWidth = dtohs(screenWidth);
1917 screenHeight = dtohs(screenHeight);
1918 sdkVersion = dtohs(sdkVersion);
1919 minorVersion = dtohs(minorVersion);
1920 smallestScreenWidthDp = dtohs(smallestScreenWidthDp);
1921 screenWidthDp = dtohs(screenWidthDp);
1922 screenHeightDp = dtohs(screenHeightDp);
1923 }
1924
swapHtoD()1925 void ResTable_config::swapHtoD() {
1926 size = htodl(size);
1927 mcc = htods(mcc);
1928 mnc = htods(mnc);
1929 density = htods(density);
1930 screenWidth = htods(screenWidth);
1931 screenHeight = htods(screenHeight);
1932 sdkVersion = htods(sdkVersion);
1933 minorVersion = htods(minorVersion);
1934 smallestScreenWidthDp = htods(smallestScreenWidthDp);
1935 screenWidthDp = htods(screenWidthDp);
1936 screenHeightDp = htods(screenHeightDp);
1937 }
1938
compareLocales(const ResTable_config & l,const ResTable_config & r)1939 /* static */ inline int compareLocales(const ResTable_config &l, const ResTable_config &r) {
1940 if (l.locale != r.locale) {
1941 return (l.locale > r.locale) ? 1 : -1;
1942 }
1943
1944 // The language & region are equal, so compare the scripts, variants and
1945 // numbering systms in this order. Comparison of variants and numbering
1946 // systems should happen very infrequently (if at all.)
1947 // The comparison code relies on memcmp low-level optimizations that make it
1948 // more efficient than strncmp.
1949 const char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'};
1950 const char *lScript = l.localeScriptWasComputed ? emptyScript : l.localeScript;
1951 const char *rScript = r.localeScriptWasComputed ? emptyScript : r.localeScript;
1952
1953 int script = memcmp(lScript, rScript, sizeof(l.localeScript));
1954 if (script) {
1955 return script;
1956 }
1957
1958 int variant = memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant));
1959 if (variant) {
1960 return variant;
1961 }
1962
1963 return memcmp(l.localeNumberingSystem, r.localeNumberingSystem,
1964 sizeof(l.localeNumberingSystem));
1965 }
1966
compare(const ResTable_config & o) const1967 int ResTable_config::compare(const ResTable_config& o) const {
1968 if (imsi != o.imsi) {
1969 return (imsi > o.imsi) ? 1 : -1;
1970 }
1971
1972 int32_t diff = compareLocales(*this, o);
1973 if (diff < 0) {
1974 return -1;
1975 }
1976 if (diff > 0) {
1977 return 1;
1978 }
1979
1980 if (screenType != o.screenType) {
1981 return (screenType > o.screenType) ? 1 : -1;
1982 }
1983 if (input != o.input) {
1984 return (input > o.input) ? 1 : -1;
1985 }
1986 if (screenSize != o.screenSize) {
1987 return (screenSize > o.screenSize) ? 1 : -1;
1988 }
1989 if (version != o.version) {
1990 return (version > o.version) ? 1 : -1;
1991 }
1992 if (screenLayout != o.screenLayout) {
1993 return (screenLayout > o.screenLayout) ? 1 : -1;
1994 }
1995 if (screenLayout2 != o.screenLayout2) {
1996 return (screenLayout2 > o.screenLayout2) ? 1 : -1;
1997 }
1998 if (colorMode != o.colorMode) {
1999 return (colorMode > o.colorMode) ? 1 : -1;
2000 }
2001 if (uiMode != o.uiMode) {
2002 return (uiMode > o.uiMode) ? 1 : -1;
2003 }
2004 if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
2005 return (smallestScreenWidthDp > o.smallestScreenWidthDp) ? 1 : -1;
2006 }
2007 if (screenSizeDp != o.screenSizeDp) {
2008 return (screenSizeDp > o.screenSizeDp) ? 1 : -1;
2009 }
2010 return 0;
2011 }
2012
compareLogical(const ResTable_config & o) const2013 int ResTable_config::compareLogical(const ResTable_config& o) const {
2014 if (mcc != o.mcc) {
2015 return mcc < o.mcc ? -1 : 1;
2016 }
2017 if (mnc != o.mnc) {
2018 return mnc < o.mnc ? -1 : 1;
2019 }
2020
2021 int diff = compareLocales(*this, o);
2022 if (diff < 0) {
2023 return -1;
2024 }
2025 if (diff > 0) {
2026 return 1;
2027 }
2028
2029 if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) {
2030 return (screenLayout & MASK_LAYOUTDIR) < (o.screenLayout & MASK_LAYOUTDIR) ? -1 : 1;
2031 }
2032 if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
2033 return smallestScreenWidthDp < o.smallestScreenWidthDp ? -1 : 1;
2034 }
2035 if (screenWidthDp != o.screenWidthDp) {
2036 return screenWidthDp < o.screenWidthDp ? -1 : 1;
2037 }
2038 if (screenHeightDp != o.screenHeightDp) {
2039 return screenHeightDp < o.screenHeightDp ? -1 : 1;
2040 }
2041 if (screenWidth != o.screenWidth) {
2042 return screenWidth < o.screenWidth ? -1 : 1;
2043 }
2044 if (screenHeight != o.screenHeight) {
2045 return screenHeight < o.screenHeight ? -1 : 1;
2046 }
2047 if (density != o.density) {
2048 return density < o.density ? -1 : 1;
2049 }
2050 if (orientation != o.orientation) {
2051 return orientation < o.orientation ? -1 : 1;
2052 }
2053 if (touchscreen != o.touchscreen) {
2054 return touchscreen < o.touchscreen ? -1 : 1;
2055 }
2056 if (input != o.input) {
2057 return input < o.input ? -1 : 1;
2058 }
2059 if (screenLayout != o.screenLayout) {
2060 return screenLayout < o.screenLayout ? -1 : 1;
2061 }
2062 if (screenLayout2 != o.screenLayout2) {
2063 return screenLayout2 < o.screenLayout2 ? -1 : 1;
2064 }
2065 if (colorMode != o.colorMode) {
2066 return colorMode < o.colorMode ? -1 : 1;
2067 }
2068 if (uiMode != o.uiMode) {
2069 return uiMode < o.uiMode ? -1 : 1;
2070 }
2071 if (version != o.version) {
2072 return version < o.version ? -1 : 1;
2073 }
2074 return 0;
2075 }
2076
diff(const ResTable_config & o) const2077 int ResTable_config::diff(const ResTable_config& o) const {
2078 int diffs = 0;
2079 if (mcc != o.mcc) diffs |= CONFIG_MCC;
2080 if (mnc != o.mnc) diffs |= CONFIG_MNC;
2081 if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION;
2082 if (density != o.density) diffs |= CONFIG_DENSITY;
2083 if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN;
2084 if (((inputFlags^o.inputFlags)&(MASK_KEYSHIDDEN|MASK_NAVHIDDEN)) != 0)
2085 diffs |= CONFIG_KEYBOARD_HIDDEN;
2086 if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD;
2087 if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION;
2088 if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE;
2089 if (version != o.version) diffs |= CONFIG_VERSION;
2090 if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) diffs |= CONFIG_LAYOUTDIR;
2091 if ((screenLayout & ~MASK_LAYOUTDIR) != (o.screenLayout & ~MASK_LAYOUTDIR)) diffs |= CONFIG_SCREEN_LAYOUT;
2092 if ((screenLayout2 & MASK_SCREENROUND) != (o.screenLayout2 & MASK_SCREENROUND)) diffs |= CONFIG_SCREEN_ROUND;
2093 if ((colorMode & MASK_WIDE_COLOR_GAMUT) != (o.colorMode & MASK_WIDE_COLOR_GAMUT)) diffs |= CONFIG_COLOR_MODE;
2094 if ((colorMode & MASK_HDR) != (o.colorMode & MASK_HDR)) diffs |= CONFIG_COLOR_MODE;
2095 if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE;
2096 if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE;
2097 if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE;
2098
2099 const int diff = compareLocales(*this, o);
2100 if (diff) diffs |= CONFIG_LOCALE;
2101
2102 return diffs;
2103 }
2104
2105 // There isn't a well specified "importance" order between variants and
2106 // scripts. We can't easily tell whether, say "en-Latn-US" is more or less
2107 // specific than "en-US-POSIX".
2108 //
2109 // We therefore arbitrarily decide to give priority to variants over
2110 // scripts since it seems more useful to do so. We will consider
2111 // "en-US-POSIX" to be more specific than "en-Latn-US".
2112 //
2113 // Unicode extension keywords are considered to be less important than
2114 // scripts and variants.
getImportanceScoreOfLocale() const2115 inline int ResTable_config::getImportanceScoreOfLocale() const {
2116 return (localeVariant[0] ? 4 : 0)
2117 + (localeScript[0] && !localeScriptWasComputed ? 2: 0)
2118 + (localeNumberingSystem[0] ? 1: 0);
2119 }
2120
isLocaleMoreSpecificThan(const ResTable_config & o) const2121 int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const {
2122 if (locale || o.locale) {
2123 if (language[0] != o.language[0]) {
2124 if (!language[0]) return -1;
2125 if (!o.language[0]) return 1;
2126 }
2127
2128 if (country[0] != o.country[0]) {
2129 if (!country[0]) return -1;
2130 if (!o.country[0]) return 1;
2131 }
2132 }
2133
2134 return getImportanceScoreOfLocale() - o.getImportanceScoreOfLocale();
2135 }
2136
isMoreSpecificThan(const ResTable_config & o) const2137 bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const {
2138 // The order of the following tests defines the importance of one
2139 // configuration parameter over another. Those tests first are more
2140 // important, trumping any values in those following them.
2141 if (imsi || o.imsi) {
2142 if (mcc != o.mcc) {
2143 if (!mcc) return false;
2144 if (!o.mcc) return true;
2145 }
2146
2147 if (mnc != o.mnc) {
2148 if (!mnc) return false;
2149 if (!o.mnc) return true;
2150 }
2151 }
2152
2153 if (locale || o.locale) {
2154 const int diff = isLocaleMoreSpecificThan(o);
2155 if (diff < 0) {
2156 return false;
2157 }
2158
2159 if (diff > 0) {
2160 return true;
2161 }
2162 }
2163
2164 if (screenLayout || o.screenLayout) {
2165 if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0) {
2166 if (!(screenLayout & MASK_LAYOUTDIR)) return false;
2167 if (!(o.screenLayout & MASK_LAYOUTDIR)) return true;
2168 }
2169 }
2170
2171 if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
2172 if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
2173 if (!smallestScreenWidthDp) return false;
2174 if (!o.smallestScreenWidthDp) return true;
2175 }
2176 }
2177
2178 if (screenSizeDp || o.screenSizeDp) {
2179 if (screenWidthDp != o.screenWidthDp) {
2180 if (!screenWidthDp) return false;
2181 if (!o.screenWidthDp) return true;
2182 }
2183
2184 if (screenHeightDp != o.screenHeightDp) {
2185 if (!screenHeightDp) return false;
2186 if (!o.screenHeightDp) return true;
2187 }
2188 }
2189
2190 if (screenLayout || o.screenLayout) {
2191 if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) {
2192 if (!(screenLayout & MASK_SCREENSIZE)) return false;
2193 if (!(o.screenLayout & MASK_SCREENSIZE)) return true;
2194 }
2195 if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0) {
2196 if (!(screenLayout & MASK_SCREENLONG)) return false;
2197 if (!(o.screenLayout & MASK_SCREENLONG)) return true;
2198 }
2199 }
2200
2201 if (screenLayout2 || o.screenLayout2) {
2202 if (((screenLayout2^o.screenLayout2) & MASK_SCREENROUND) != 0) {
2203 if (!(screenLayout2 & MASK_SCREENROUND)) return false;
2204 if (!(o.screenLayout2 & MASK_SCREENROUND)) return true;
2205 }
2206 }
2207
2208 if (colorMode || o.colorMode) {
2209 if (((colorMode^o.colorMode) & MASK_HDR) != 0) {
2210 if (!(colorMode & MASK_HDR)) return false;
2211 if (!(o.colorMode & MASK_HDR)) return true;
2212 }
2213 if (((colorMode^o.colorMode) & MASK_WIDE_COLOR_GAMUT) != 0) {
2214 if (!(colorMode & MASK_WIDE_COLOR_GAMUT)) return false;
2215 if (!(o.colorMode & MASK_WIDE_COLOR_GAMUT)) return true;
2216 }
2217 }
2218
2219 if (orientation != o.orientation) {
2220 if (!orientation) return false;
2221 if (!o.orientation) return true;
2222 }
2223
2224 if (uiMode || o.uiMode) {
2225 if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0) {
2226 if (!(uiMode & MASK_UI_MODE_TYPE)) return false;
2227 if (!(o.uiMode & MASK_UI_MODE_TYPE)) return true;
2228 }
2229 if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0) {
2230 if (!(uiMode & MASK_UI_MODE_NIGHT)) return false;
2231 if (!(o.uiMode & MASK_UI_MODE_NIGHT)) return true;
2232 }
2233 }
2234
2235 // density is never 'more specific'
2236 // as the default just equals 160
2237
2238 if (touchscreen != o.touchscreen) {
2239 if (!touchscreen) return false;
2240 if (!o.touchscreen) return true;
2241 }
2242
2243 if (input || o.input) {
2244 if (((inputFlags^o.inputFlags) & MASK_KEYSHIDDEN) != 0) {
2245 if (!(inputFlags & MASK_KEYSHIDDEN)) return false;
2246 if (!(o.inputFlags & MASK_KEYSHIDDEN)) return true;
2247 }
2248
2249 if (((inputFlags^o.inputFlags) & MASK_NAVHIDDEN) != 0) {
2250 if (!(inputFlags & MASK_NAVHIDDEN)) return false;
2251 if (!(o.inputFlags & MASK_NAVHIDDEN)) return true;
2252 }
2253
2254 if (keyboard != o.keyboard) {
2255 if (!keyboard) return false;
2256 if (!o.keyboard) return true;
2257 }
2258
2259 if (navigation != o.navigation) {
2260 if (!navigation) return false;
2261 if (!o.navigation) return true;
2262 }
2263 }
2264
2265 if (screenSize || o.screenSize) {
2266 if (screenWidth != o.screenWidth) {
2267 if (!screenWidth) return false;
2268 if (!o.screenWidth) return true;
2269 }
2270
2271 if (screenHeight != o.screenHeight) {
2272 if (!screenHeight) return false;
2273 if (!o.screenHeight) return true;
2274 }
2275 }
2276
2277 if (version || o.version) {
2278 if (sdkVersion != o.sdkVersion) {
2279 if (!sdkVersion) return false;
2280 if (!o.sdkVersion) return true;
2281 }
2282
2283 if (minorVersion != o.minorVersion) {
2284 if (!minorVersion) return false;
2285 if (!o.minorVersion) return true;
2286 }
2287 }
2288 return false;
2289 }
2290
2291 // Codes for specially handled languages and regions
2292 static const char kEnglish[2] = {'e', 'n'}; // packed version of "en"
2293 static const char kUnitedStates[2] = {'U', 'S'}; // packed version of "US"
2294 static const char kFilipino[2] = {'\xAD', '\x05'}; // packed version of "fil"
2295 static const char kTagalog[2] = {'t', 'l'}; // packed version of "tl"
2296
2297 // Checks if two language or region codes are identical
areIdentical(const char code1[2],const char code2[2])2298 inline bool areIdentical(const char code1[2], const char code2[2]) {
2299 return code1[0] == code2[0] && code1[1] == code2[1];
2300 }
2301
langsAreEquivalent(const char lang1[2],const char lang2[2])2302 inline bool langsAreEquivalent(const char lang1[2], const char lang2[2]) {
2303 return areIdentical(lang1, lang2) ||
2304 (areIdentical(lang1, kTagalog) && areIdentical(lang2, kFilipino)) ||
2305 (areIdentical(lang1, kFilipino) && areIdentical(lang2, kTagalog));
2306 }
2307
isLocaleBetterThan(const ResTable_config & o,const ResTable_config * requested) const2308 bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
2309 const ResTable_config* requested) const {
2310 if (requested->locale == 0) {
2311 // The request doesn't have a locale, so no resource is better
2312 // than the other.
2313 return false;
2314 }
2315
2316 if (locale == 0 && o.locale == 0) {
2317 // The locale part of both resources is empty, so none is better
2318 // than the other.
2319 return false;
2320 }
2321
2322 // Non-matching locales have been filtered out, so both resources
2323 // match the requested locale.
2324 //
2325 // Because of the locale-related checks in match() and the checks, we know
2326 // that:
2327 // 1) The resource languages are either empty or match the request;
2328 // and
2329 // 2) If the request's script is known, the resource scripts are either
2330 // unknown or match the request.
2331
2332 if (!langsAreEquivalent(language, o.language)) {
2333 // The languages of the two resources are not equivalent. If we are
2334 // here, we can only assume that the two resources matched the request
2335 // because one doesn't have a language and the other has a matching
2336 // language.
2337 //
2338 // We consider the one that has the language specified a better match.
2339 //
2340 // The exception is that we consider no-language resources a better match
2341 // for US English and similar locales than locales that are a descendant
2342 // of Internatinal English (en-001), since no-language resources are
2343 // where the US English resource have traditionally lived for most apps.
2344 if (areIdentical(requested->language, kEnglish)) {
2345 if (areIdentical(requested->country, kUnitedStates)) {
2346 // For US English itself, we consider a no-locale resource a
2347 // better match if the other resource has a country other than
2348 // US specified.
2349 if (language[0] != '\0') {
2350 return country[0] == '\0' || areIdentical(country, kUnitedStates);
2351 } else {
2352 return !(o.country[0] == '\0' || areIdentical(o.country, kUnitedStates));
2353 }
2354 } else if (localeDataIsCloseToUsEnglish(requested->country)) {
2355 if (language[0] != '\0') {
2356 return localeDataIsCloseToUsEnglish(country);
2357 } else {
2358 return !localeDataIsCloseToUsEnglish(o.country);
2359 }
2360 }
2361 }
2362 return (language[0] != '\0');
2363 }
2364
2365 // If we are here, both the resources have an equivalent non-empty language
2366 // to the request.
2367 //
2368 // Because the languages are equivalent, computeScript() always returns a
2369 // non-empty script for languages it knows about, and we have passed the
2370 // script checks in match(), the scripts are either all unknown or are all
2371 // the same. So we can't gain anything by checking the scripts. We need to
2372 // check the region and variant.
2373
2374 // See if any of the regions is better than the other.
2375 const int region_comparison = localeDataCompareRegions(
2376 country, o.country,
2377 requested->language, requested->localeScript, requested->country);
2378 if (region_comparison != 0) {
2379 return (region_comparison > 0);
2380 }
2381
2382 // The regions are the same. Try the variant.
2383 const bool localeMatches = strncmp(
2384 localeVariant, requested->localeVariant, sizeof(localeVariant)) == 0;
2385 const bool otherMatches = strncmp(
2386 o.localeVariant, requested->localeVariant, sizeof(localeVariant)) == 0;
2387 if (localeMatches != otherMatches) {
2388 return localeMatches;
2389 }
2390
2391 // The variants are the same, try numbering system.
2392 const bool localeNumsysMatches = strncmp(localeNumberingSystem,
2393 requested->localeNumberingSystem,
2394 sizeof(localeNumberingSystem)) == 0;
2395 const bool otherNumsysMatches = strncmp(o.localeNumberingSystem,
2396 requested->localeNumberingSystem,
2397 sizeof(localeNumberingSystem)) == 0;
2398 if (localeNumsysMatches != otherNumsysMatches) {
2399 return localeNumsysMatches;
2400 }
2401
2402 // Finally, the languages, although equivalent, may still be different
2403 // (like for Tagalog and Filipino). Identical is better than just
2404 // equivalent.
2405 if (areIdentical(language, requested->language)
2406 && !areIdentical(o.language, requested->language)) {
2407 return true;
2408 }
2409
2410 return false;
2411 }
2412
isBetterThan(const ResTable_config & o,const ResTable_config * requested) const2413 bool ResTable_config::isBetterThan(const ResTable_config& o,
2414 const ResTable_config* requested) const {
2415 if (requested) {
2416 if (imsi || o.imsi) {
2417 if ((mcc != o.mcc) && requested->mcc) {
2418 return (mcc);
2419 }
2420
2421 if ((mnc != o.mnc) && requested->mnc) {
2422 return (mnc);
2423 }
2424 }
2425
2426 if (isLocaleBetterThan(o, requested)) {
2427 return true;
2428 }
2429
2430 if (screenLayout || o.screenLayout) {
2431 if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0
2432 && (requested->screenLayout & MASK_LAYOUTDIR)) {
2433 int myLayoutDir = screenLayout & MASK_LAYOUTDIR;
2434 int oLayoutDir = o.screenLayout & MASK_LAYOUTDIR;
2435 return (myLayoutDir > oLayoutDir);
2436 }
2437 }
2438
2439 if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
2440 // The configuration closest to the actual size is best.
2441 // We assume that larger configs have already been filtered
2442 // out at this point. That means we just want the largest one.
2443 if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
2444 return smallestScreenWidthDp > o.smallestScreenWidthDp;
2445 }
2446 }
2447
2448 if (screenSizeDp || o.screenSizeDp) {
2449 // "Better" is based on the sum of the difference between both
2450 // width and height from the requested dimensions. We are
2451 // assuming the invalid configs (with smaller dimens) have
2452 // already been filtered. Note that if a particular dimension
2453 // is unspecified, we will end up with a large value (the
2454 // difference between 0 and the requested dimension), which is
2455 // good since we will prefer a config that has specified a
2456 // dimension value.
2457 int myDelta = 0, otherDelta = 0;
2458 if (requested->screenWidthDp) {
2459 myDelta += requested->screenWidthDp - screenWidthDp;
2460 otherDelta += requested->screenWidthDp - o.screenWidthDp;
2461 }
2462 if (requested->screenHeightDp) {
2463 myDelta += requested->screenHeightDp - screenHeightDp;
2464 otherDelta += requested->screenHeightDp - o.screenHeightDp;
2465 }
2466 if (kDebugTableSuperNoisy) {
2467 ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d",
2468 screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp,
2469 requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta);
2470 }
2471 if (myDelta != otherDelta) {
2472 return myDelta < otherDelta;
2473 }
2474 }
2475
2476 if (screenLayout || o.screenLayout) {
2477 if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0
2478 && (requested->screenLayout & MASK_SCREENSIZE)) {
2479 // A little backwards compatibility here: undefined is
2480 // considered equivalent to normal. But only if the
2481 // requested size is at least normal; otherwise, small
2482 // is better than the default.
2483 int mySL = (screenLayout & MASK_SCREENSIZE);
2484 int oSL = (o.screenLayout & MASK_SCREENSIZE);
2485 int fixedMySL = mySL;
2486 int fixedOSL = oSL;
2487 if ((requested->screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) {
2488 if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL;
2489 if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL;
2490 }
2491 // For screen size, the best match is the one that is
2492 // closest to the requested screen size, but not over
2493 // (the not over part is dealt with in match() below).
2494 if (fixedMySL == fixedOSL) {
2495 // If the two are the same, but 'this' is actually
2496 // undefined, then the other is really a better match.
2497 if (mySL == 0) return false;
2498 return true;
2499 }
2500 if (fixedMySL != fixedOSL) {
2501 return fixedMySL > fixedOSL;
2502 }
2503 }
2504 if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0
2505 && (requested->screenLayout & MASK_SCREENLONG)) {
2506 return (screenLayout & MASK_SCREENLONG);
2507 }
2508 }
2509
2510 if (screenLayout2 || o.screenLayout2) {
2511 if (((screenLayout2^o.screenLayout2) & MASK_SCREENROUND) != 0 &&
2512 (requested->screenLayout2 & MASK_SCREENROUND)) {
2513 return screenLayout2 & MASK_SCREENROUND;
2514 }
2515 }
2516
2517 if (colorMode || o.colorMode) {
2518 if (((colorMode^o.colorMode) & MASK_WIDE_COLOR_GAMUT) != 0 &&
2519 (requested->colorMode & MASK_WIDE_COLOR_GAMUT)) {
2520 return colorMode & MASK_WIDE_COLOR_GAMUT;
2521 }
2522 if (((colorMode^o.colorMode) & MASK_HDR) != 0 &&
2523 (requested->colorMode & MASK_HDR)) {
2524 return colorMode & MASK_HDR;
2525 }
2526 }
2527
2528 if ((orientation != o.orientation) && requested->orientation) {
2529 return (orientation);
2530 }
2531
2532 if (uiMode || o.uiMode) {
2533 if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0
2534 && (requested->uiMode & MASK_UI_MODE_TYPE)) {
2535 return (uiMode & MASK_UI_MODE_TYPE);
2536 }
2537 if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0
2538 && (requested->uiMode & MASK_UI_MODE_NIGHT)) {
2539 return (uiMode & MASK_UI_MODE_NIGHT);
2540 }
2541 }
2542
2543 if (screenType || o.screenType) {
2544 if (density != o.density) {
2545 // Use the system default density (DENSITY_MEDIUM, 160dpi) if none specified.
2546 const int thisDensity = density ? density : int(ResTable_config::DENSITY_MEDIUM);
2547 const int otherDensity = o.density ? o.density : int(ResTable_config::DENSITY_MEDIUM);
2548
2549 // We always prefer DENSITY_ANY over scaling a density bucket.
2550 if (thisDensity == ResTable_config::DENSITY_ANY) {
2551 return true;
2552 } else if (otherDensity == ResTable_config::DENSITY_ANY) {
2553 return false;
2554 }
2555
2556 int requestedDensity = requested->density;
2557 if (requested->density == 0 ||
2558 requested->density == ResTable_config::DENSITY_ANY) {
2559 requestedDensity = ResTable_config::DENSITY_MEDIUM;
2560 }
2561
2562 // DENSITY_ANY is now dealt with. We should look to
2563 // pick a density bucket and potentially scale it.
2564 // Any density is potentially useful
2565 // because the system will scale it. Scaling down
2566 // is generally better than scaling up.
2567 int h = thisDensity;
2568 int l = otherDensity;
2569 bool bImBigger = true;
2570 if (l > h) {
2571 int t = h;
2572 h = l;
2573 l = t;
2574 bImBigger = false;
2575 }
2576
2577 if (requestedDensity >= h) {
2578 // requested value higher than both l and h, give h
2579 return bImBigger;
2580 }
2581 if (l >= requestedDensity) {
2582 // requested value lower than both l and h, give l
2583 return !bImBigger;
2584 }
2585 // saying that scaling down is 2x better than up
2586 if (((2 * l) - requestedDensity) * h > requestedDensity * requestedDensity) {
2587 return !bImBigger;
2588 } else {
2589 return bImBigger;
2590 }
2591 }
2592
2593 if ((touchscreen != o.touchscreen) && requested->touchscreen) {
2594 return (touchscreen);
2595 }
2596 }
2597
2598 if (input || o.input) {
2599 const int keysHidden = inputFlags & MASK_KEYSHIDDEN;
2600 const int oKeysHidden = o.inputFlags & MASK_KEYSHIDDEN;
2601 if (keysHidden != oKeysHidden) {
2602 const int reqKeysHidden =
2603 requested->inputFlags & MASK_KEYSHIDDEN;
2604 if (reqKeysHidden) {
2605
2606 if (!keysHidden) return false;
2607 if (!oKeysHidden) return true;
2608 // For compatibility, we count KEYSHIDDEN_NO as being
2609 // the same as KEYSHIDDEN_SOFT. Here we disambiguate
2610 // these by making an exact match more specific.
2611 if (reqKeysHidden == keysHidden) return true;
2612 if (reqKeysHidden == oKeysHidden) return false;
2613 }
2614 }
2615
2616 const int navHidden = inputFlags & MASK_NAVHIDDEN;
2617 const int oNavHidden = o.inputFlags & MASK_NAVHIDDEN;
2618 if (navHidden != oNavHidden) {
2619 const int reqNavHidden =
2620 requested->inputFlags & MASK_NAVHIDDEN;
2621 if (reqNavHidden) {
2622
2623 if (!navHidden) return false;
2624 if (!oNavHidden) return true;
2625 }
2626 }
2627
2628 if ((keyboard != o.keyboard) && requested->keyboard) {
2629 return (keyboard);
2630 }
2631
2632 if ((navigation != o.navigation) && requested->navigation) {
2633 return (navigation);
2634 }
2635 }
2636
2637 if (screenSize || o.screenSize) {
2638 // "Better" is based on the sum of the difference between both
2639 // width and height from the requested dimensions. We are
2640 // assuming the invalid configs (with smaller sizes) have
2641 // already been filtered. Note that if a particular dimension
2642 // is unspecified, we will end up with a large value (the
2643 // difference between 0 and the requested dimension), which is
2644 // good since we will prefer a config that has specified a
2645 // size value.
2646 int myDelta = 0, otherDelta = 0;
2647 if (requested->screenWidth) {
2648 myDelta += requested->screenWidth - screenWidth;
2649 otherDelta += requested->screenWidth - o.screenWidth;
2650 }
2651 if (requested->screenHeight) {
2652 myDelta += requested->screenHeight - screenHeight;
2653 otherDelta += requested->screenHeight - o.screenHeight;
2654 }
2655 if (myDelta != otherDelta) {
2656 return myDelta < otherDelta;
2657 }
2658 }
2659
2660 if (version || o.version) {
2661 if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) {
2662 return (sdkVersion > o.sdkVersion);
2663 }
2664
2665 if ((minorVersion != o.minorVersion) &&
2666 requested->minorVersion) {
2667 return (minorVersion);
2668 }
2669 }
2670
2671 return false;
2672 }
2673 return isMoreSpecificThan(o);
2674 }
2675
match(const ResTable_config & settings) const2676 bool ResTable_config::match(const ResTable_config& settings) const {
2677 if (imsi != 0) {
2678 if (mcc != 0 && mcc != settings.mcc) {
2679 return false;
2680 }
2681 if (mnc != 0 && mnc != settings.mnc) {
2682 return false;
2683 }
2684 }
2685 if (locale != 0) {
2686 // Don't consider country and variants when deciding matches.
2687 // (Theoretically, the variant can also affect the script. For
2688 // example, "ar-alalc97" probably implies the Latin script, but since
2689 // CLDR doesn't support getting likely scripts for that, we'll assume
2690 // the variant doesn't change the script.)
2691 //
2692 // If two configs differ only in their country and variant,
2693 // they can be weeded out in the isMoreSpecificThan test.
2694 if (!langsAreEquivalent(language, settings.language)) {
2695 return false;
2696 }
2697
2698 // For backward compatibility and supporting private-use locales, we
2699 // fall back to old behavior if we couldn't determine the script for
2700 // either of the desired locale or the provided locale. But if we could determine
2701 // the scripts, they should be the same for the locales to match.
2702 bool countriesMustMatch = false;
2703 char computed_script[4];
2704 const char* script;
2705 if (settings.localeScript[0] == '\0') { // could not determine the request's script
2706 countriesMustMatch = true;
2707 } else {
2708 if (localeScript[0] == '\0' && !localeScriptWasComputed) {
2709 // script was not provided or computed, so we try to compute it
2710 localeDataComputeScript(computed_script, language, country);
2711 if (computed_script[0] == '\0') { // we could not compute the script
2712 countriesMustMatch = true;
2713 } else {
2714 script = computed_script;
2715 }
2716 } else { // script was provided, so just use it
2717 script = localeScript;
2718 }
2719 }
2720
2721 if (countriesMustMatch) {
2722 if (country[0] != '\0' && !areIdentical(country, settings.country)) {
2723 return false;
2724 }
2725 } else {
2726 if (memcmp(script, settings.localeScript, sizeof(settings.localeScript)) != 0) {
2727 return false;
2728 }
2729 }
2730 }
2731
2732 if (screenConfig != 0) {
2733 const int layoutDir = screenLayout&MASK_LAYOUTDIR;
2734 const int setLayoutDir = settings.screenLayout&MASK_LAYOUTDIR;
2735 if (layoutDir != 0 && layoutDir != setLayoutDir) {
2736 return false;
2737 }
2738
2739 const int screenSize = screenLayout&MASK_SCREENSIZE;
2740 const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE;
2741 // Any screen sizes for larger screens than the setting do not
2742 // match.
2743 if (screenSize != 0 && screenSize > setScreenSize) {
2744 return false;
2745 }
2746
2747 const int screenLong = screenLayout&MASK_SCREENLONG;
2748 const int setScreenLong = settings.screenLayout&MASK_SCREENLONG;
2749 if (screenLong != 0 && screenLong != setScreenLong) {
2750 return false;
2751 }
2752
2753 const int uiModeType = uiMode&MASK_UI_MODE_TYPE;
2754 const int setUiModeType = settings.uiMode&MASK_UI_MODE_TYPE;
2755 if (uiModeType != 0 && uiModeType != setUiModeType) {
2756 return false;
2757 }
2758
2759 const int uiModeNight = uiMode&MASK_UI_MODE_NIGHT;
2760 const int setUiModeNight = settings.uiMode&MASK_UI_MODE_NIGHT;
2761 if (uiModeNight != 0 && uiModeNight != setUiModeNight) {
2762 return false;
2763 }
2764
2765 if (smallestScreenWidthDp != 0
2766 && smallestScreenWidthDp > settings.smallestScreenWidthDp) {
2767 return false;
2768 }
2769 }
2770
2771 if (screenConfig2 != 0) {
2772 const int screenRound = screenLayout2 & MASK_SCREENROUND;
2773 const int setScreenRound = settings.screenLayout2 & MASK_SCREENROUND;
2774 if (screenRound != 0 && screenRound != setScreenRound) {
2775 return false;
2776 }
2777
2778 const int hdr = colorMode & MASK_HDR;
2779 const int setHdr = settings.colorMode & MASK_HDR;
2780 if (hdr != 0 && hdr != setHdr) {
2781 return false;
2782 }
2783
2784 const int wideColorGamut = colorMode & MASK_WIDE_COLOR_GAMUT;
2785 const int setWideColorGamut = settings.colorMode & MASK_WIDE_COLOR_GAMUT;
2786 if (wideColorGamut != 0 && wideColorGamut != setWideColorGamut) {
2787 return false;
2788 }
2789 }
2790
2791 if (screenSizeDp != 0) {
2792 if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) {
2793 if (kDebugTableSuperNoisy) {
2794 ALOGI("Filtering out width %d in requested %d", screenWidthDp,
2795 settings.screenWidthDp);
2796 }
2797 return false;
2798 }
2799 if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) {
2800 if (kDebugTableSuperNoisy) {
2801 ALOGI("Filtering out height %d in requested %d", screenHeightDp,
2802 settings.screenHeightDp);
2803 }
2804 return false;
2805 }
2806 }
2807 if (screenType != 0) {
2808 if (orientation != 0 && orientation != settings.orientation) {
2809 return false;
2810 }
2811 // density always matches - we can scale it. See isBetterThan
2812 if (touchscreen != 0 && touchscreen != settings.touchscreen) {
2813 return false;
2814 }
2815 }
2816 if (input != 0) {
2817 const int keysHidden = inputFlags&MASK_KEYSHIDDEN;
2818 const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN;
2819 if (keysHidden != 0 && keysHidden != setKeysHidden) {
2820 // For compatibility, we count a request for KEYSHIDDEN_NO as also
2821 // matching the more recent KEYSHIDDEN_SOFT. Basically
2822 // KEYSHIDDEN_NO means there is some kind of keyboard available.
2823 if (kDebugTableSuperNoisy) {
2824 ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden);
2825 }
2826 if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) {
2827 if (kDebugTableSuperNoisy) {
2828 ALOGI("No match!");
2829 }
2830 return false;
2831 }
2832 }
2833 const int navHidden = inputFlags&MASK_NAVHIDDEN;
2834 const int setNavHidden = settings.inputFlags&MASK_NAVHIDDEN;
2835 if (navHidden != 0 && navHidden != setNavHidden) {
2836 return false;
2837 }
2838 if (keyboard != 0 && keyboard != settings.keyboard) {
2839 return false;
2840 }
2841 if (navigation != 0 && navigation != settings.navigation) {
2842 return false;
2843 }
2844 }
2845 if (screenSize != 0) {
2846 if (screenWidth != 0 && screenWidth > settings.screenWidth) {
2847 return false;
2848 }
2849 if (screenHeight != 0 && screenHeight > settings.screenHeight) {
2850 return false;
2851 }
2852 }
2853 if (version != 0) {
2854 if (sdkVersion != 0 && sdkVersion > settings.sdkVersion) {
2855 return false;
2856 }
2857 if (minorVersion != 0 && minorVersion != settings.minorVersion) {
2858 return false;
2859 }
2860 }
2861 return true;
2862 }
2863
appendDirLocale(String8 & out) const2864 void ResTable_config::appendDirLocale(String8& out) const {
2865 if (!language[0]) {
2866 return;
2867 }
2868 const bool scriptWasProvided = localeScript[0] != '\0' && !localeScriptWasComputed;
2869 if (!scriptWasProvided && !localeVariant[0] && !localeNumberingSystem[0]) {
2870 // Legacy format.
2871 if (out.size() > 0) {
2872 out.append("-");
2873 }
2874
2875 char buf[4];
2876 size_t len = unpackLanguage(buf);
2877 out.append(buf, len);
2878
2879 if (country[0]) {
2880 out.append("-r");
2881 len = unpackRegion(buf);
2882 out.append(buf, len);
2883 }
2884 return;
2885 }
2886
2887 // We are writing the modified BCP 47 tag.
2888 // It starts with 'b+' and uses '+' as a separator.
2889
2890 if (out.size() > 0) {
2891 out.append("-");
2892 }
2893 out.append("b+");
2894
2895 char buf[4];
2896 size_t len = unpackLanguage(buf);
2897 out.append(buf, len);
2898
2899 if (scriptWasProvided) {
2900 out.append("+");
2901 out.append(localeScript, sizeof(localeScript));
2902 }
2903
2904 if (country[0]) {
2905 out.append("+");
2906 len = unpackRegion(buf);
2907 out.append(buf, len);
2908 }
2909
2910 if (localeVariant[0]) {
2911 out.append("+");
2912 out.append(localeVariant, strnlen(localeVariant, sizeof(localeVariant)));
2913 }
2914
2915 if (localeNumberingSystem[0]) {
2916 out.append("+u+nu+");
2917 out.append(localeNumberingSystem,
2918 strnlen(localeNumberingSystem, sizeof(localeNumberingSystem)));
2919 }
2920 }
2921
getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN],bool canonicalize) const2922 void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool canonicalize) const {
2923 memset(str, 0, RESTABLE_MAX_LOCALE_LEN);
2924
2925 // This represents the "any" locale value, which has traditionally been
2926 // represented by the empty string.
2927 if (language[0] == '\0' && country[0] == '\0') {
2928 return;
2929 }
2930
2931 size_t charsWritten = 0;
2932 if (language[0] != '\0') {
2933 if (canonicalize && areIdentical(language, kTagalog)) {
2934 // Replace Tagalog with Filipino if we are canonicalizing
2935 str[0] = 'f'; str[1] = 'i'; str[2] = 'l'; str[3] = '\0'; // 3-letter code for Filipino
2936 charsWritten += 3;
2937 } else {
2938 charsWritten += unpackLanguage(str);
2939 }
2940 }
2941
2942 if (localeScript[0] != '\0' && !localeScriptWasComputed) {
2943 if (charsWritten > 0) {
2944 str[charsWritten++] = '-';
2945 }
2946 memcpy(str + charsWritten, localeScript, sizeof(localeScript));
2947 charsWritten += sizeof(localeScript);
2948 }
2949
2950 if (country[0] != '\0') {
2951 if (charsWritten > 0) {
2952 str[charsWritten++] = '-';
2953 }
2954 charsWritten += unpackRegion(str + charsWritten);
2955 }
2956
2957 if (localeVariant[0] != '\0') {
2958 if (charsWritten > 0) {
2959 str[charsWritten++] = '-';
2960 }
2961 memcpy(str + charsWritten, localeVariant, sizeof(localeVariant));
2962 charsWritten += strnlen(str + charsWritten, sizeof(localeVariant));
2963 }
2964
2965 // Add Unicode extension only if at least one other locale component is present
2966 if (localeNumberingSystem[0] != '\0' && charsWritten > 0) {
2967 static constexpr char NU_PREFIX[] = "-u-nu-";
2968 static constexpr size_t NU_PREFIX_LEN = sizeof(NU_PREFIX) - 1;
2969 memcpy(str + charsWritten, NU_PREFIX, NU_PREFIX_LEN);
2970 charsWritten += NU_PREFIX_LEN;
2971 memcpy(str + charsWritten, localeNumberingSystem, sizeof(localeNumberingSystem));
2972 }
2973 }
2974
2975 struct LocaleParserState {
2976 enum State : uint8_t {
2977 BASE, UNICODE_EXTENSION, IGNORE_THE_REST
2978 } parserState;
2979 enum UnicodeState : uint8_t {
2980 /* Initial state after the Unicode singleton is detected. Either a keyword
2981 * or an attribute is expected. */
2982 NO_KEY,
2983 /* Unicode extension key (but not attribute) is expected. Next states:
2984 * NO_KEY, IGNORE_KEY or NUMBERING_SYSTEM. */
2985 EXPECT_KEY,
2986 /* A key is detected, however it is not supported for now. Ignore its
2987 * value. Next states: IGNORE_KEY or NUMBERING_SYSTEM. */
2988 IGNORE_KEY,
2989 /* Numbering system key was detected. Store its value in the configuration
2990 * localeNumberingSystem field. Next state: EXPECT_KEY */
2991 NUMBERING_SYSTEM
2992 } unicodeState;
2993
LocaleParserStateandroid::LocaleParserState2994 LocaleParserState(): parserState(BASE), unicodeState(NO_KEY) {}
2995 };
2996
assignLocaleComponent(ResTable_config * config,const char * start,size_t size,LocaleParserState state)2997 /* static */ inline LocaleParserState assignLocaleComponent(ResTable_config* config,
2998 const char* start, size_t size, LocaleParserState state) {
2999
3000 /* It is assumed that this function is not invoked with state.parserState
3001 * set to IGNORE_THE_REST. The condition is checked by setBcp47Locale
3002 * function. */
3003
3004 if (state.parserState == LocaleParserState::UNICODE_EXTENSION) {
3005 switch (size) {
3006 case 1:
3007 /* Other BCP 47 extensions are not supported at the moment */
3008 state.parserState = LocaleParserState::IGNORE_THE_REST;
3009 break;
3010 case 2:
3011 if (state.unicodeState == LocaleParserState::NO_KEY ||
3012 state.unicodeState == LocaleParserState::EXPECT_KEY) {
3013 /* Analyze Unicode extension key. Currently only 'nu'
3014 * (numbering system) is supported.*/
3015 if ((start[0] == 'n' || start[0] == 'N') &&
3016 (start[1] == 'u' || start[1] == 'U')) {
3017 state.unicodeState = LocaleParserState::NUMBERING_SYSTEM;
3018 } else {
3019 state.unicodeState = LocaleParserState::IGNORE_KEY;
3020 }
3021 } else {
3022 /* Keys are not allowed in other state allowed, ignore the rest. */
3023 state.parserState = LocaleParserState::IGNORE_THE_REST;
3024 }
3025 break;
3026 case 3:
3027 case 4:
3028 case 5:
3029 case 6:
3030 case 7:
3031 case 8:
3032 switch (state.unicodeState) {
3033 case LocaleParserState::NUMBERING_SYSTEM:
3034 /* Accept only the first occurrence of the numbering system. */
3035 if (config->localeNumberingSystem[0] == '\0') {
3036 for (size_t i = 0; i < size; ++i) {
3037 config->localeNumberingSystem[i] = tolower(start[i]);
3038 }
3039 state.unicodeState = LocaleParserState::EXPECT_KEY;
3040 } else {
3041 state.parserState = LocaleParserState::IGNORE_THE_REST;
3042 }
3043 break;
3044 case LocaleParserState::IGNORE_KEY:
3045 /* Unsupported Unicode keyword. Ignore. */
3046 state.unicodeState = LocaleParserState::EXPECT_KEY;
3047 break;
3048 case LocaleParserState::EXPECT_KEY:
3049 /* A keyword followed by an attribute is not allowed. */
3050 state.parserState = LocaleParserState::IGNORE_THE_REST;
3051 break;
3052 case LocaleParserState::NO_KEY:
3053 /* Extension attribute. Do nothing. */
3054 break;
3055 default:
3056 break;
3057 }
3058 break;
3059 default:
3060 /* Unexpected field length - ignore the rest and treat as an error */
3061 state.parserState = LocaleParserState::IGNORE_THE_REST;
3062 }
3063 return state;
3064 }
3065
3066 switch (size) {
3067 case 0:
3068 state.parserState = LocaleParserState::IGNORE_THE_REST;
3069 break;
3070 case 1:
3071 state.parserState = (start[0] == 'u' || start[0] == 'U')
3072 ? LocaleParserState::UNICODE_EXTENSION
3073 : LocaleParserState::IGNORE_THE_REST;
3074 break;
3075 case 2:
3076 case 3:
3077 config->language[0] ? config->packRegion(start) : config->packLanguage(start);
3078 break;
3079 case 4:
3080 if ('0' <= start[0] && start[0] <= '9') {
3081 // this is a variant, so fall through
3082 } else {
3083 config->localeScript[0] = toupper(start[0]);
3084 for (size_t i = 1; i < 4; ++i) {
3085 config->localeScript[i] = tolower(start[i]);
3086 }
3087 break;
3088 }
3089 FALLTHROUGH_INTENDED;
3090 case 5:
3091 case 6:
3092 case 7:
3093 case 8:
3094 for (size_t i = 0; i < size; ++i) {
3095 config->localeVariant[i] = tolower(start[i]);
3096 }
3097 break;
3098 default:
3099 state.parserState = LocaleParserState::IGNORE_THE_REST;
3100 }
3101
3102 return state;
3103 }
3104
setBcp47Locale(const char * in)3105 void ResTable_config::setBcp47Locale(const char* in) {
3106 clearLocale();
3107
3108 const char* start = in;
3109 LocaleParserState state;
3110 while (const char* separator = strchr(start, '-')) {
3111 const size_t size = separator - start;
3112 state = assignLocaleComponent(this, start, size, state);
3113 if (state.parserState == LocaleParserState::IGNORE_THE_REST) {
3114 fprintf(stderr, "Invalid BCP-47 locale string: %s\n", in);
3115 break;
3116 }
3117 start = (separator + 1);
3118 }
3119
3120 if (state.parserState != LocaleParserState::IGNORE_THE_REST) {
3121 const size_t size = strlen(start);
3122 assignLocaleComponent(this, start, size, state);
3123 }
3124
3125 localeScriptWasComputed = (localeScript[0] == '\0');
3126 if (localeScriptWasComputed) {
3127 computeScript();
3128 }
3129 }
3130
toString() const3131 String8 ResTable_config::toString() const {
3132 String8 res;
3133
3134 if (mcc != 0) {
3135 if (res.size() > 0) res.append("-");
3136 res.appendFormat("mcc%d", dtohs(mcc));
3137 }
3138 if (mnc != 0) {
3139 if (res.size() > 0) res.append("-");
3140 res.appendFormat("mnc%d", dtohs(mnc));
3141 }
3142
3143 appendDirLocale(res);
3144
3145 if ((screenLayout&MASK_LAYOUTDIR) != 0) {
3146 if (res.size() > 0) res.append("-");
3147 switch (screenLayout&ResTable_config::MASK_LAYOUTDIR) {
3148 case ResTable_config::LAYOUTDIR_LTR:
3149 res.append("ldltr");
3150 break;
3151 case ResTable_config::LAYOUTDIR_RTL:
3152 res.append("ldrtl");
3153 break;
3154 default:
3155 res.appendFormat("layoutDir=%d",
3156 dtohs(screenLayout&ResTable_config::MASK_LAYOUTDIR));
3157 break;
3158 }
3159 }
3160 if (smallestScreenWidthDp != 0) {
3161 if (res.size() > 0) res.append("-");
3162 res.appendFormat("sw%ddp", dtohs(smallestScreenWidthDp));
3163 }
3164 if (screenWidthDp != 0) {
3165 if (res.size() > 0) res.append("-");
3166 res.appendFormat("w%ddp", dtohs(screenWidthDp));
3167 }
3168 if (screenHeightDp != 0) {
3169 if (res.size() > 0) res.append("-");
3170 res.appendFormat("h%ddp", dtohs(screenHeightDp));
3171 }
3172 if ((screenLayout&MASK_SCREENSIZE) != SCREENSIZE_ANY) {
3173 if (res.size() > 0) res.append("-");
3174 switch (screenLayout&ResTable_config::MASK_SCREENSIZE) {
3175 case ResTable_config::SCREENSIZE_SMALL:
3176 res.append("small");
3177 break;
3178 case ResTable_config::SCREENSIZE_NORMAL:
3179 res.append("normal");
3180 break;
3181 case ResTable_config::SCREENSIZE_LARGE:
3182 res.append("large");
3183 break;
3184 case ResTable_config::SCREENSIZE_XLARGE:
3185 res.append("xlarge");
3186 break;
3187 default:
3188 res.appendFormat("screenLayoutSize=%d",
3189 dtohs(screenLayout&ResTable_config::MASK_SCREENSIZE));
3190 break;
3191 }
3192 }
3193 if ((screenLayout&MASK_SCREENLONG) != 0) {
3194 if (res.size() > 0) res.append("-");
3195 switch (screenLayout&ResTable_config::MASK_SCREENLONG) {
3196 case ResTable_config::SCREENLONG_NO:
3197 res.append("notlong");
3198 break;
3199 case ResTable_config::SCREENLONG_YES:
3200 res.append("long");
3201 break;
3202 default:
3203 res.appendFormat("screenLayoutLong=%d",
3204 dtohs(screenLayout&ResTable_config::MASK_SCREENLONG));
3205 break;
3206 }
3207 }
3208 if ((screenLayout2&MASK_SCREENROUND) != 0) {
3209 if (res.size() > 0) res.append("-");
3210 switch (screenLayout2&MASK_SCREENROUND) {
3211 case SCREENROUND_NO:
3212 res.append("notround");
3213 break;
3214 case SCREENROUND_YES:
3215 res.append("round");
3216 break;
3217 default:
3218 res.appendFormat("screenRound=%d", dtohs(screenLayout2&MASK_SCREENROUND));
3219 break;
3220 }
3221 }
3222 if ((colorMode&MASK_WIDE_COLOR_GAMUT) != 0) {
3223 if (res.size() > 0) res.append("-");
3224 switch (colorMode&MASK_WIDE_COLOR_GAMUT) {
3225 case ResTable_config::WIDE_COLOR_GAMUT_NO:
3226 res.append("nowidecg");
3227 break;
3228 case ResTable_config::WIDE_COLOR_GAMUT_YES:
3229 res.append("widecg");
3230 break;
3231 default:
3232 res.appendFormat("wideColorGamut=%d", dtohs(colorMode&MASK_WIDE_COLOR_GAMUT));
3233 break;
3234 }
3235 }
3236 if ((colorMode&MASK_HDR) != 0) {
3237 if (res.size() > 0) res.append("-");
3238 switch (colorMode&MASK_HDR) {
3239 case ResTable_config::HDR_NO:
3240 res.append("lowdr");
3241 break;
3242 case ResTable_config::HDR_YES:
3243 res.append("highdr");
3244 break;
3245 default:
3246 res.appendFormat("hdr=%d", dtohs(colorMode&MASK_HDR));
3247 break;
3248 }
3249 }
3250 if (orientation != ORIENTATION_ANY) {
3251 if (res.size() > 0) res.append("-");
3252 switch (orientation) {
3253 case ResTable_config::ORIENTATION_PORT:
3254 res.append("port");
3255 break;
3256 case ResTable_config::ORIENTATION_LAND:
3257 res.append("land");
3258 break;
3259 case ResTable_config::ORIENTATION_SQUARE:
3260 res.append("square");
3261 break;
3262 default:
3263 res.appendFormat("orientation=%d", dtohs(orientation));
3264 break;
3265 }
3266 }
3267 if ((uiMode&MASK_UI_MODE_TYPE) != UI_MODE_TYPE_ANY) {
3268 if (res.size() > 0) res.append("-");
3269 switch (uiMode&ResTable_config::MASK_UI_MODE_TYPE) {
3270 case ResTable_config::UI_MODE_TYPE_DESK:
3271 res.append("desk");
3272 break;
3273 case ResTable_config::UI_MODE_TYPE_CAR:
3274 res.append("car");
3275 break;
3276 case ResTable_config::UI_MODE_TYPE_TELEVISION:
3277 res.append("television");
3278 break;
3279 case ResTable_config::UI_MODE_TYPE_APPLIANCE:
3280 res.append("appliance");
3281 break;
3282 case ResTable_config::UI_MODE_TYPE_WATCH:
3283 res.append("watch");
3284 break;
3285 case ResTable_config::UI_MODE_TYPE_VR_HEADSET:
3286 res.append("vrheadset");
3287 break;
3288 default:
3289 res.appendFormat("uiModeType=%d",
3290 dtohs(screenLayout&ResTable_config::MASK_UI_MODE_TYPE));
3291 break;
3292 }
3293 }
3294 if ((uiMode&MASK_UI_MODE_NIGHT) != 0) {
3295 if (res.size() > 0) res.append("-");
3296 switch (uiMode&ResTable_config::MASK_UI_MODE_NIGHT) {
3297 case ResTable_config::UI_MODE_NIGHT_NO:
3298 res.append("notnight");
3299 break;
3300 case ResTable_config::UI_MODE_NIGHT_YES:
3301 res.append("night");
3302 break;
3303 default:
3304 res.appendFormat("uiModeNight=%d",
3305 dtohs(uiMode&MASK_UI_MODE_NIGHT));
3306 break;
3307 }
3308 }
3309 if (density != DENSITY_DEFAULT) {
3310 if (res.size() > 0) res.append("-");
3311 switch (density) {
3312 case ResTable_config::DENSITY_LOW:
3313 res.append("ldpi");
3314 break;
3315 case ResTable_config::DENSITY_MEDIUM:
3316 res.append("mdpi");
3317 break;
3318 case ResTable_config::DENSITY_TV:
3319 res.append("tvdpi");
3320 break;
3321 case ResTable_config::DENSITY_HIGH:
3322 res.append("hdpi");
3323 break;
3324 case ResTable_config::DENSITY_XHIGH:
3325 res.append("xhdpi");
3326 break;
3327 case ResTable_config::DENSITY_XXHIGH:
3328 res.append("xxhdpi");
3329 break;
3330 case ResTable_config::DENSITY_XXXHIGH:
3331 res.append("xxxhdpi");
3332 break;
3333 case ResTable_config::DENSITY_NONE:
3334 res.append("nodpi");
3335 break;
3336 case ResTable_config::DENSITY_ANY:
3337 res.append("anydpi");
3338 break;
3339 default:
3340 res.appendFormat("%ddpi", dtohs(density));
3341 break;
3342 }
3343 }
3344 if (touchscreen != TOUCHSCREEN_ANY) {
3345 if (res.size() > 0) res.append("-");
3346 switch (touchscreen) {
3347 case ResTable_config::TOUCHSCREEN_NOTOUCH:
3348 res.append("notouch");
3349 break;
3350 case ResTable_config::TOUCHSCREEN_FINGER:
3351 res.append("finger");
3352 break;
3353 case ResTable_config::TOUCHSCREEN_STYLUS:
3354 res.append("stylus");
3355 break;
3356 default:
3357 res.appendFormat("touchscreen=%d", dtohs(touchscreen));
3358 break;
3359 }
3360 }
3361 if ((inputFlags&MASK_KEYSHIDDEN) != 0) {
3362 if (res.size() > 0) res.append("-");
3363 switch (inputFlags&MASK_KEYSHIDDEN) {
3364 case ResTable_config::KEYSHIDDEN_NO:
3365 res.append("keysexposed");
3366 break;
3367 case ResTable_config::KEYSHIDDEN_YES:
3368 res.append("keyshidden");
3369 break;
3370 case ResTable_config::KEYSHIDDEN_SOFT:
3371 res.append("keyssoft");
3372 break;
3373 }
3374 }
3375 if (keyboard != KEYBOARD_ANY) {
3376 if (res.size() > 0) res.append("-");
3377 switch (keyboard) {
3378 case ResTable_config::KEYBOARD_NOKEYS:
3379 res.append("nokeys");
3380 break;
3381 case ResTable_config::KEYBOARD_QWERTY:
3382 res.append("qwerty");
3383 break;
3384 case ResTable_config::KEYBOARD_12KEY:
3385 res.append("12key");
3386 break;
3387 default:
3388 res.appendFormat("keyboard=%d", dtohs(keyboard));
3389 break;
3390 }
3391 }
3392 if ((inputFlags&MASK_NAVHIDDEN) != 0) {
3393 if (res.size() > 0) res.append("-");
3394 switch (inputFlags&MASK_NAVHIDDEN) {
3395 case ResTable_config::NAVHIDDEN_NO:
3396 res.append("navexposed");
3397 break;
3398 case ResTable_config::NAVHIDDEN_YES:
3399 res.append("navhidden");
3400 break;
3401 default:
3402 res.appendFormat("inputFlagsNavHidden=%d",
3403 dtohs(inputFlags&MASK_NAVHIDDEN));
3404 break;
3405 }
3406 }
3407 if (navigation != NAVIGATION_ANY) {
3408 if (res.size() > 0) res.append("-");
3409 switch (navigation) {
3410 case ResTable_config::NAVIGATION_NONAV:
3411 res.append("nonav");
3412 break;
3413 case ResTable_config::NAVIGATION_DPAD:
3414 res.append("dpad");
3415 break;
3416 case ResTable_config::NAVIGATION_TRACKBALL:
3417 res.append("trackball");
3418 break;
3419 case ResTable_config::NAVIGATION_WHEEL:
3420 res.append("wheel");
3421 break;
3422 default:
3423 res.appendFormat("navigation=%d", dtohs(navigation));
3424 break;
3425 }
3426 }
3427 if (screenSize != 0) {
3428 if (res.size() > 0) res.append("-");
3429 res.appendFormat("%dx%d", dtohs(screenWidth), dtohs(screenHeight));
3430 }
3431 if (version != 0) {
3432 if (res.size() > 0) res.append("-");
3433 res.appendFormat("v%d", dtohs(sdkVersion));
3434 if (minorVersion != 0) {
3435 res.appendFormat(".%d", dtohs(minorVersion));
3436 }
3437 }
3438
3439 return res;
3440 }
3441
3442 // --------------------------------------------------------------------
3443 // --------------------------------------------------------------------
3444 // --------------------------------------------------------------------
3445
3446 struct ResTable::Header
3447 {
Headerandroid::ResTable::Header3448 explicit Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL),
3449 resourceIDMap(NULL), resourceIDMapSize(0) { }
3450
~Headerandroid::ResTable::Header3451 ~Header()
3452 {
3453 free(resourceIDMap);
3454 }
3455
3456 const ResTable* const owner;
3457 void* ownedData;
3458 const ResTable_header* header;
3459 size_t size;
3460 const uint8_t* dataEnd;
3461 size_t index;
3462 int32_t cookie;
3463
3464 ResStringPool values;
3465 uint32_t* resourceIDMap;
3466 size_t resourceIDMapSize;
3467 };
3468
3469 struct ResTable::Entry {
3470 ResTable_config config;
3471 const ResTable_entry* entry;
3472 const ResTable_type* type;
3473 uint32_t specFlags;
3474 const Package* package;
3475
3476 StringPoolRef typeStr;
3477 StringPoolRef keyStr;
3478 };
3479
3480 struct ResTable::Type
3481 {
Typeandroid::ResTable::Type3482 Type(const Header* _header, const Package* _package, size_t count)
3483 : header(_header), package(_package), entryCount(count),
3484 typeSpec(NULL), typeSpecFlags(NULL) { }
3485 const Header* const header;
3486 const Package* const package;
3487 const size_t entryCount;
3488 const ResTable_typeSpec* typeSpec;
3489 const uint32_t* typeSpecFlags;
3490 IdmapEntries idmapEntries;
3491 Vector<const ResTable_type*> configs;
3492 };
3493
3494 struct ResTable::Package
3495 {
Packageandroid::ResTable::Package3496 Package(ResTable* _owner, const Header* _header, const ResTable_package* _package)
3497 : owner(_owner), header(_header), package(_package), typeIdOffset(0) {
3498 if (dtohs(package->header.headerSize) == sizeof(*package)) {
3499 // The package structure is the same size as the definition.
3500 // This means it contains the typeIdOffset field.
3501 typeIdOffset = package->typeIdOffset;
3502 }
3503 }
3504
3505 const ResTable* const owner;
3506 const Header* const header;
3507 const ResTable_package* const package;
3508
3509 ResStringPool typeStrings;
3510 ResStringPool keyStrings;
3511
3512 size_t typeIdOffset;
3513 bool definesOverlayable = false;
3514 };
3515
3516 // A group of objects describing a particular resource package.
3517 // The first in 'package' is always the root object (from the resource
3518 // table that defined the package); the ones after are skins on top of it.
3519 struct ResTable::PackageGroup
3520 {
PackageGroupandroid::ResTable::PackageGroup3521 PackageGroup(
3522 ResTable* _owner, const String16& _name, uint32_t _id,
3523 bool appAsLib, bool _isSystemAsset, bool _isDynamic)
3524 : owner(_owner)
3525 , name(_name)
3526 , id(_id)
3527 , largestTypeId(0)
3528 , dynamicRefTable(static_cast<uint8_t>(_id), appAsLib)
3529 , isSystemAsset(_isSystemAsset)
3530 , isDynamic(_isDynamic)
3531 { }
3532
~PackageGroupandroid::ResTable::PackageGroup3533 ~PackageGroup() {
3534 clearBagCache();
3535 const size_t numTypes = types.size();
3536 for (size_t i = 0; i < numTypes; i++) {
3537 TypeList& typeList = types.editItemAt(i);
3538 const size_t numInnerTypes = typeList.size();
3539 for (size_t j = 0; j < numInnerTypes; j++) {
3540 if (typeList[j]->package->owner == owner) {
3541 delete typeList[j];
3542 }
3543 }
3544 typeList.clear();
3545 }
3546
3547 const size_t N = packages.size();
3548 for (size_t i=0; i<N; i++) {
3549 Package* pkg = packages[i];
3550 if (pkg->owner == owner) {
3551 delete pkg;
3552 }
3553 }
3554 }
3555
3556 /**
3557 * Clear all cache related data that depends on parameters/configuration.
3558 * This includes the bag caches and filtered types.
3559 */
clearBagCacheandroid::ResTable::PackageGroup3560 void clearBagCache() {
3561 for (size_t i = 0; i < typeCacheEntries.size(); i++) {
3562 if (kDebugTableNoisy) {
3563 printf("type=%zu\n", i);
3564 }
3565 const TypeList& typeList = types[i];
3566 if (!typeList.isEmpty()) {
3567 TypeCacheEntry& cacheEntry = typeCacheEntries.editItemAt(i);
3568
3569 // Reset the filtered configurations.
3570 cacheEntry.filteredConfigs.clear();
3571
3572 bag_set** typeBags = cacheEntry.cachedBags;
3573 if (kDebugTableNoisy) {
3574 printf("typeBags=%p\n", typeBags);
3575 }
3576
3577 if (typeBags) {
3578 const size_t N = typeList[0]->entryCount;
3579 if (kDebugTableNoisy) {
3580 printf("type->entryCount=%zu\n", N);
3581 }
3582 for (size_t j = 0; j < N; j++) {
3583 if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF) {
3584 free(typeBags[j]);
3585 }
3586 }
3587 free(typeBags);
3588 cacheEntry.cachedBags = NULL;
3589 }
3590 }
3591 }
3592 }
3593
findType16android::ResTable::PackageGroup3594 ssize_t findType16(const char16_t* type, size_t len) const {
3595 const size_t N = packages.size();
3596 for (size_t i = 0; i < N; i++) {
3597 ssize_t index = packages[i]->typeStrings.indexOfString(type, len);
3598 if (index >= 0) {
3599 return index + packages[i]->typeIdOffset;
3600 }
3601 }
3602 return -1;
3603 }
3604
3605 const ResTable* const owner;
3606 String16 const name;
3607 uint32_t const id;
3608
3609 // This is mainly used to keep track of the loaded packages
3610 // and to clean them up properly. Accessing resources happens from
3611 // the 'types' array.
3612 Vector<Package*> packages;
3613
3614 ByteBucketArray<TypeList> types;
3615
3616 uint8_t largestTypeId;
3617
3618 // Cached objects dependent on the parameters/configuration of this ResTable.
3619 // Gets cleared whenever the parameters/configuration changes.
3620 // These are stored here in a parallel structure because the data in `types` may
3621 // be shared by other ResTable's (framework resources are shared this way).
3622 ByteBucketArray<TypeCacheEntry> typeCacheEntries;
3623
3624 // The table mapping dynamic references to resolved references for
3625 // this package group.
3626 // TODO: We may be able to support dynamic references in overlays
3627 // by having these tables in a per-package scope rather than
3628 // per-package-group.
3629 DynamicRefTable dynamicRefTable;
3630
3631 // If the package group comes from a system asset. Used in
3632 // determining non-system locales.
3633 const bool isSystemAsset;
3634 const bool isDynamic;
3635 };
3636
Theme(const ResTable & table)3637 ResTable::Theme::Theme(const ResTable& table)
3638 : mTable(table)
3639 , mTypeSpecFlags(0)
3640 {
3641 memset(mPackages, 0, sizeof(mPackages));
3642 }
3643
~Theme()3644 ResTable::Theme::~Theme()
3645 {
3646 for (size_t i=0; i<Res_MAXPACKAGE; i++) {
3647 package_info* pi = mPackages[i];
3648 if (pi != NULL) {
3649 free_package(pi);
3650 }
3651 }
3652 }
3653
free_package(package_info * pi)3654 void ResTable::Theme::free_package(package_info* pi)
3655 {
3656 for (size_t j = 0; j <= Res_MAXTYPE; j++) {
3657 theme_entry* te = pi->types[j].entries;
3658 if (te != NULL) {
3659 free(te);
3660 }
3661 }
3662 free(pi);
3663 }
3664
copy_package(package_info * pi)3665 ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi)
3666 {
3667 package_info* newpi = (package_info*)malloc(sizeof(package_info));
3668 for (size_t j = 0; j <= Res_MAXTYPE; j++) {
3669 size_t cnt = pi->types[j].numEntries;
3670 newpi->types[j].numEntries = cnt;
3671 theme_entry* te = pi->types[j].entries;
3672 size_t cnt_max = SIZE_MAX / sizeof(theme_entry);
3673 if (te != NULL && (cnt < 0xFFFFFFFF-1) && (cnt < cnt_max)) {
3674 theme_entry* newte = (theme_entry*)malloc(cnt*sizeof(theme_entry));
3675 newpi->types[j].entries = newte;
3676 memcpy(newte, te, cnt*sizeof(theme_entry));
3677 } else {
3678 newpi->types[j].entries = NULL;
3679 }
3680 }
3681 return newpi;
3682 }
3683
applyStyle(uint32_t resID,bool force)3684 status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
3685 {
3686 const bag_entry* bag;
3687 uint32_t bagTypeSpecFlags = 0;
3688 mTable.lock();
3689 const ssize_t N = mTable.getBagLocked(resID, &bag, &bagTypeSpecFlags);
3690 if (kDebugTableNoisy) {
3691 ALOGV("Applying style 0x%08x to theme %p, count=%zu", resID, this, N);
3692 }
3693 if (N < 0) {
3694 mTable.unlock();
3695 return N;
3696 }
3697
3698 mTypeSpecFlags |= bagTypeSpecFlags;
3699
3700 uint32_t curPackage = 0xffffffff;
3701 ssize_t curPackageIndex = 0;
3702 package_info* curPI = NULL;
3703 uint32_t curType = 0xffffffff;
3704 size_t numEntries = 0;
3705 theme_entry* curEntries = NULL;
3706
3707 const bag_entry* end = bag + N;
3708 while (bag < end) {
3709 const uint32_t attrRes = bag->map.name.ident;
3710 const uint32_t p = Res_GETPACKAGE(attrRes);
3711 const uint32_t t = Res_GETTYPE(attrRes);
3712 const uint32_t e = Res_GETENTRY(attrRes);
3713
3714 if (curPackage != p) {
3715 const ssize_t pidx = mTable.getResourcePackageIndex(attrRes);
3716 if (pidx < 0) {
3717 ALOGE("Style contains key with bad package: 0x%08x\n", attrRes);
3718 bag++;
3719 continue;
3720 }
3721 curPackage = p;
3722 curPackageIndex = pidx;
3723 curPI = mPackages[pidx];
3724 if (curPI == NULL) {
3725 curPI = (package_info*)malloc(sizeof(package_info));
3726 memset(curPI, 0, sizeof(*curPI));
3727 mPackages[pidx] = curPI;
3728 }
3729 curType = 0xffffffff;
3730 }
3731 if (curType != t) {
3732 if (t > Res_MAXTYPE) {
3733 ALOGE("Style contains key with bad type: 0x%08x\n", attrRes);
3734 bag++;
3735 continue;
3736 }
3737 curType = t;
3738 curEntries = curPI->types[t].entries;
3739 if (curEntries == NULL) {
3740 PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex];
3741 const TypeList& typeList = grp->types[t];
3742 size_t cnt = typeList.isEmpty() ? 0 : typeList[0]->entryCount;
3743 size_t cnt_max = SIZE_MAX / sizeof(theme_entry);
3744 size_t buff_size = (cnt < cnt_max && cnt < 0xFFFFFFFF-1) ?
3745 cnt*sizeof(theme_entry) : 0;
3746 curEntries = (theme_entry*)malloc(buff_size);
3747 memset(curEntries, Res_value::TYPE_NULL, buff_size);
3748 curPI->types[t].numEntries = cnt;
3749 curPI->types[t].entries = curEntries;
3750 }
3751 numEntries = curPI->types[t].numEntries;
3752 }
3753 if (e >= numEntries) {
3754 ALOGE("Style contains key with bad entry: 0x%08x\n", attrRes);
3755 bag++;
3756 continue;
3757 }
3758 theme_entry* curEntry = curEntries + e;
3759 if (kDebugTableNoisy) {
3760 ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x",
3761 attrRes, bag->map.value.dataType, bag->map.value.data,
3762 curEntry->value.dataType);
3763 }
3764 if (force || (curEntry->value.dataType == Res_value::TYPE_NULL
3765 && curEntry->value.data != Res_value::DATA_NULL_EMPTY)) {
3766 curEntry->stringBlock = bag->stringBlock;
3767 curEntry->typeSpecFlags |= bagTypeSpecFlags;
3768 curEntry->value = bag->map.value;
3769 }
3770
3771 bag++;
3772 }
3773
3774 mTable.unlock();
3775
3776 if (kDebugTableTheme) {
3777 ALOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this);
3778 dumpToLog();
3779 }
3780
3781 return NO_ERROR;
3782 }
3783
setTo(const Theme & other)3784 status_t ResTable::Theme::setTo(const Theme& other)
3785 {
3786 if (kDebugTableTheme) {
3787 ALOGI("Setting theme %p from theme %p...\n", this, &other);
3788 dumpToLog();
3789 other.dumpToLog();
3790 }
3791
3792 if (&mTable == &other.mTable) {
3793 for (size_t i=0; i<Res_MAXPACKAGE; i++) {
3794 if (mPackages[i] != NULL) {
3795 free_package(mPackages[i]);
3796 }
3797 if (other.mPackages[i] != NULL) {
3798 mPackages[i] = copy_package(other.mPackages[i]);
3799 } else {
3800 mPackages[i] = NULL;
3801 }
3802 }
3803 } else {
3804 // @todo: need to really implement this, not just copy
3805 // the system package (which is still wrong because it isn't
3806 // fixing up resource references).
3807 for (size_t i=0; i<Res_MAXPACKAGE; i++) {
3808 if (mPackages[i] != NULL) {
3809 free_package(mPackages[i]);
3810 }
3811 if (i == 0 && other.mPackages[i] != NULL) {
3812 mPackages[i] = copy_package(other.mPackages[i]);
3813 } else {
3814 mPackages[i] = NULL;
3815 }
3816 }
3817 }
3818
3819 mTypeSpecFlags = other.mTypeSpecFlags;
3820
3821 if (kDebugTableTheme) {
3822 ALOGI("Final theme:");
3823 dumpToLog();
3824 }
3825
3826 return NO_ERROR;
3827 }
3828
clear()3829 status_t ResTable::Theme::clear()
3830 {
3831 if (kDebugTableTheme) {
3832 ALOGI("Clearing theme %p...\n", this);
3833 dumpToLog();
3834 }
3835
3836 for (size_t i = 0; i < Res_MAXPACKAGE; i++) {
3837 if (mPackages[i] != NULL) {
3838 free_package(mPackages[i]);
3839 mPackages[i] = NULL;
3840 }
3841 }
3842
3843 mTypeSpecFlags = 0;
3844
3845 if (kDebugTableTheme) {
3846 ALOGI("Final theme:");
3847 dumpToLog();
3848 }
3849
3850 return NO_ERROR;
3851 }
3852
getAttribute(uint32_t resID,Res_value * outValue,uint32_t * outTypeSpecFlags) const3853 ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
3854 uint32_t* outTypeSpecFlags) const
3855 {
3856 int cnt = 20;
3857
3858 if (outTypeSpecFlags != NULL) *outTypeSpecFlags = 0;
3859
3860 do {
3861 const ssize_t p = mTable.getResourcePackageIndex(resID);
3862 const uint32_t t = Res_GETTYPE(resID);
3863 const uint32_t e = Res_GETENTRY(resID);
3864
3865 if (kDebugTableTheme) {
3866 ALOGI("Looking up attr 0x%08x in theme %p", resID, this);
3867 }
3868
3869 if (p >= 0) {
3870 const package_info* const pi = mPackages[p];
3871 if (kDebugTableTheme) {
3872 ALOGI("Found package: %p", pi);
3873 }
3874 if (pi != NULL) {
3875 if (kDebugTableTheme) {
3876 ALOGI("Desired type index is %u in avail %zu", t, Res_MAXTYPE + 1);
3877 }
3878 if (t <= Res_MAXTYPE) {
3879 const type_info& ti = pi->types[t];
3880 if (kDebugTableTheme) {
3881 ALOGI("Desired entry index is %u in avail %zu", e, ti.numEntries);
3882 }
3883 if (e < ti.numEntries) {
3884 const theme_entry& te = ti.entries[e];
3885 if (outTypeSpecFlags != NULL) {
3886 *outTypeSpecFlags |= te.typeSpecFlags;
3887 }
3888 if (kDebugTableTheme) {
3889 ALOGI("Theme value: type=0x%x, data=0x%08x",
3890 te.value.dataType, te.value.data);
3891 }
3892 const uint8_t type = te.value.dataType;
3893 if (type == Res_value::TYPE_ATTRIBUTE) {
3894 if (cnt > 0) {
3895 cnt--;
3896 resID = te.value.data;
3897 continue;
3898 }
3899 ALOGW("Too many attribute references, stopped at: 0x%08x\n", resID);
3900 return BAD_INDEX;
3901 } else if (type != Res_value::TYPE_NULL
3902 || te.value.data == Res_value::DATA_NULL_EMPTY) {
3903 *outValue = te.value;
3904 return te.stringBlock;
3905 }
3906 return BAD_INDEX;
3907 }
3908 }
3909 }
3910 }
3911 break;
3912
3913 } while (true);
3914
3915 return BAD_INDEX;
3916 }
3917
resolveAttributeReference(Res_value * inOutValue,ssize_t blockIndex,uint32_t * outLastRef,uint32_t * inoutTypeSpecFlags,ResTable_config * inoutConfig) const3918 ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue,
3919 ssize_t blockIndex, uint32_t* outLastRef,
3920 uint32_t* inoutTypeSpecFlags, ResTable_config* inoutConfig) const
3921 {
3922 //printf("Resolving type=0x%x\n", inOutValue->dataType);
3923 if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) {
3924 uint32_t newTypeSpecFlags;
3925 blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags);
3926 if (kDebugTableTheme) {
3927 ALOGI("Resolving attr reference: blockIndex=%d, type=0x%x, data=0x%x\n",
3928 (int)blockIndex, (int)inOutValue->dataType, inOutValue->data);
3929 }
3930 if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags;
3931 //printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType);
3932 if (blockIndex < 0) {
3933 return blockIndex;
3934 }
3935 }
3936 return mTable.resolveReference(inOutValue, blockIndex, outLastRef,
3937 inoutTypeSpecFlags, inoutConfig);
3938 }
3939
getChangingConfigurations() const3940 uint32_t ResTable::Theme::getChangingConfigurations() const
3941 {
3942 return mTypeSpecFlags;
3943 }
3944
dumpToLog() const3945 void ResTable::Theme::dumpToLog() const
3946 {
3947 ALOGI("Theme %p:\n", this);
3948 for (size_t i=0; i<Res_MAXPACKAGE; i++) {
3949 package_info* pi = mPackages[i];
3950 if (pi == NULL) continue;
3951
3952 ALOGI(" Package #0x%02x:\n", (int)(i + 1));
3953 for (size_t j = 0; j <= Res_MAXTYPE; j++) {
3954 type_info& ti = pi->types[j];
3955 if (ti.numEntries == 0) continue;
3956 ALOGI(" Type #0x%02x:\n", (int)(j + 1));
3957 for (size_t k = 0; k < ti.numEntries; k++) {
3958 const theme_entry& te = ti.entries[k];
3959 if (te.value.dataType == Res_value::TYPE_NULL) continue;
3960 ALOGI(" 0x%08x: t=0x%x, d=0x%08x (block=%d)\n",
3961 (int)Res_MAKEID(i, j, k),
3962 te.value.dataType, (int)te.value.data, (int)te.stringBlock);
3963 }
3964 }
3965 }
3966 }
3967
ResTable()3968 ResTable::ResTable()
3969 : mError(NO_INIT), mNextPackageId(2)
3970 {
3971 memset(&mParams, 0, sizeof(mParams));
3972 memset(mPackageMap, 0, sizeof(mPackageMap));
3973 if (kDebugTableSuperNoisy) {
3974 ALOGI("Creating ResTable %p\n", this);
3975 }
3976 }
3977
ResTable(const void * data,size_t size,const int32_t cookie,bool copyData)3978 ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool copyData)
3979 : mError(NO_INIT), mNextPackageId(2)
3980 {
3981 memset(&mParams, 0, sizeof(mParams));
3982 memset(mPackageMap, 0, sizeof(mPackageMap));
3983 addInternal(data, size, NULL, 0, false, cookie, copyData);
3984 LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
3985 if (kDebugTableSuperNoisy) {
3986 ALOGI("Creating ResTable %p\n", this);
3987 }
3988 }
3989
~ResTable()3990 ResTable::~ResTable()
3991 {
3992 if (kDebugTableSuperNoisy) {
3993 ALOGI("Destroying ResTable in %p\n", this);
3994 }
3995 uninit();
3996 }
3997
getResourcePackageIndex(uint32_t resID) const3998 inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const
3999 {
4000 return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1;
4001 }
4002
getResourcePackageIndexFromPackage(uint8_t packageID) const4003 inline ssize_t ResTable::getResourcePackageIndexFromPackage(uint8_t packageID) const
4004 {
4005 return ((ssize_t)mPackageMap[packageID])-1;
4006 }
4007
add(const void * data,size_t size,const int32_t cookie,bool copyData)4008 status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) {
4009 return addInternal(data, size, NULL, 0, false, cookie, copyData);
4010 }
4011
add(const void * data,size_t size,const void * idmapData,size_t idmapDataSize,const int32_t cookie,bool copyData,bool appAsLib)4012 status_t ResTable::add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
4013 const int32_t cookie, bool copyData, bool appAsLib) {
4014 return addInternal(data, size, idmapData, idmapDataSize, appAsLib, cookie, copyData);
4015 }
4016
add(Asset * asset,const int32_t cookie,bool copyData)4017 status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) {
4018 const void* data = asset->getBuffer(true);
4019 if (data == NULL) {
4020 ALOGW("Unable to get buffer of resource asset file");
4021 return UNKNOWN_ERROR;
4022 }
4023
4024 return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, false, 0, cookie,
4025 copyData);
4026 }
4027
add(Asset * asset,Asset * idmapAsset,const int32_t cookie,bool copyData,bool appAsLib,bool isSystemAsset)4028 status_t ResTable::add(
4029 Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData,
4030 bool appAsLib, bool isSystemAsset) {
4031 const void* data = asset->getBuffer(true);
4032 if (data == NULL) {
4033 ALOGW("Unable to get buffer of resource asset file");
4034 return UNKNOWN_ERROR;
4035 }
4036
4037 size_t idmapSize = 0;
4038 const void* idmapData = NULL;
4039 if (idmapAsset != NULL) {
4040 idmapData = idmapAsset->getBuffer(true);
4041 if (idmapData == NULL) {
4042 ALOGW("Unable to get buffer of idmap asset file");
4043 return UNKNOWN_ERROR;
4044 }
4045 idmapSize = static_cast<size_t>(idmapAsset->getLength());
4046 }
4047
4048 return addInternal(data, static_cast<size_t>(asset->getLength()),
4049 idmapData, idmapSize, appAsLib, cookie, copyData, isSystemAsset);
4050 }
4051
add(ResTable * src,bool isSystemAsset)4052 status_t ResTable::add(ResTable* src, bool isSystemAsset)
4053 {
4054 mError = src->mError;
4055
4056 for (size_t i=0; i < src->mHeaders.size(); i++) {
4057 mHeaders.add(src->mHeaders[i]);
4058 }
4059
4060 for (size_t i=0; i < src->mPackageGroups.size(); i++) {
4061 PackageGroup* srcPg = src->mPackageGroups[i];
4062 PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id,
4063 false /* appAsLib */, isSystemAsset || srcPg->isSystemAsset, srcPg->isDynamic);
4064 for (size_t j=0; j<srcPg->packages.size(); j++) {
4065 pg->packages.add(srcPg->packages[j]);
4066 }
4067
4068 for (size_t j = 0; j < srcPg->types.size(); j++) {
4069 if (srcPg->types[j].isEmpty()) {
4070 continue;
4071 }
4072
4073 TypeList& typeList = pg->types.editItemAt(j);
4074 typeList.appendVector(srcPg->types[j]);
4075 }
4076 pg->dynamicRefTable.addMappings(srcPg->dynamicRefTable);
4077 pg->largestTypeId = max(pg->largestTypeId, srcPg->largestTypeId);
4078 mPackageGroups.add(pg);
4079 }
4080
4081 memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap));
4082
4083 return mError;
4084 }
4085
addEmpty(const int32_t cookie)4086 status_t ResTable::addEmpty(const int32_t cookie) {
4087 Header* header = new Header(this);
4088 header->index = mHeaders.size();
4089 header->cookie = cookie;
4090 header->values.setToEmpty();
4091 header->ownedData = calloc(1, sizeof(ResTable_header));
4092
4093 ResTable_header* resHeader = (ResTable_header*) header->ownedData;
4094 resHeader->header.type = RES_TABLE_TYPE;
4095 resHeader->header.headerSize = sizeof(ResTable_header);
4096 resHeader->header.size = sizeof(ResTable_header);
4097
4098 header->header = (const ResTable_header*) resHeader;
4099 mHeaders.add(header);
4100 return (mError=NO_ERROR);
4101 }
4102
addInternal(const void * data,size_t dataSize,const void * idmapData,size_t idmapDataSize,bool appAsLib,const int32_t cookie,bool copyData,bool isSystemAsset)4103 status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize,
4104 bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset)
4105 {
4106 if (!data) {
4107 return NO_ERROR;
4108 }
4109
4110 if (dataSize < sizeof(ResTable_header)) {
4111 ALOGE("Invalid data. Size(%d) is smaller than a ResTable_header(%d).",
4112 (int) dataSize, (int) sizeof(ResTable_header));
4113 return UNKNOWN_ERROR;
4114 }
4115
4116 Header* header = new Header(this);
4117 header->index = mHeaders.size();
4118 header->cookie = cookie;
4119 if (idmapData != NULL) {
4120 header->resourceIDMap = (uint32_t*) malloc(idmapDataSize);
4121 if (header->resourceIDMap == NULL) {
4122 delete header;
4123 return (mError = NO_MEMORY);
4124 }
4125 memcpy(header->resourceIDMap, idmapData, idmapDataSize);
4126 header->resourceIDMapSize = idmapDataSize;
4127 }
4128 mHeaders.add(header);
4129
4130 const bool notDeviceEndian = htods(0xf0) != 0xf0;
4131
4132 if (kDebugLoadTableNoisy) {
4133 ALOGV("Adding resources to ResTable: data=%p, size=%zu, cookie=%d, copy=%d "
4134 "idmap=%p\n", data, dataSize, cookie, copyData, idmapData);
4135 }
4136
4137 if (copyData || notDeviceEndian) {
4138 header->ownedData = malloc(dataSize);
4139 if (header->ownedData == NULL) {
4140 return (mError=NO_MEMORY);
4141 }
4142 memcpy(header->ownedData, data, dataSize);
4143 data = header->ownedData;
4144 }
4145
4146 header->header = (const ResTable_header*)data;
4147 header->size = dtohl(header->header->header.size);
4148 if (kDebugLoadTableSuperNoisy) {
4149 ALOGI("Got size %zu, again size 0x%x, raw size 0x%x\n", header->size,
4150 dtohl(header->header->header.size), header->header->header.size);
4151 }
4152 if (kDebugLoadTableNoisy) {
4153 ALOGV("Loading ResTable @%p:\n", header->header);
4154 }
4155 if (dtohs(header->header->header.headerSize) > header->size
4156 || header->size > dataSize) {
4157 ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n",
4158 (int)dtohs(header->header->header.headerSize),
4159 (int)header->size, (int)dataSize);
4160 return (mError=BAD_TYPE);
4161 }
4162 if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) {
4163 ALOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n",
4164 (int)dtohs(header->header->header.headerSize),
4165 (int)header->size);
4166 return (mError=BAD_TYPE);
4167 }
4168 header->dataEnd = ((const uint8_t*)header->header) + header->size;
4169
4170 // Iterate through all chunks.
4171 size_t curPackage = 0;
4172
4173 const ResChunk_header* chunk =
4174 (const ResChunk_header*)(((const uint8_t*)header->header)
4175 + dtohs(header->header->header.headerSize));
4176 while (((const uint8_t*)chunk) <= (header->dataEnd-sizeof(ResChunk_header)) &&
4177 ((const uint8_t*)chunk) <= (header->dataEnd-dtohl(chunk->size))) {
4178 status_t err = validate_chunk(chunk, sizeof(ResChunk_header), header->dataEnd, "ResTable");
4179 if (err != NO_ERROR) {
4180 return (mError=err);
4181 }
4182 if (kDebugTableNoisy) {
4183 ALOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n",
4184 dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size),
4185 (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)));
4186 }
4187 const size_t csize = dtohl(chunk->size);
4188 const uint16_t ctype = dtohs(chunk->type);
4189 if (ctype == RES_STRING_POOL_TYPE) {
4190 if (header->values.getError() != NO_ERROR) {
4191 // Only use the first string chunk; ignore any others that
4192 // may appear.
4193 status_t err = header->values.setTo(chunk, csize);
4194 if (err != NO_ERROR) {
4195 return (mError=err);
4196 }
4197 } else {
4198 ALOGW("Multiple string chunks found in resource table.");
4199 }
4200 } else if (ctype == RES_TABLE_PACKAGE_TYPE) {
4201 if (curPackage >= dtohl(header->header->packageCount)) {
4202 ALOGW("More package chunks were found than the %d declared in the header.",
4203 dtohl(header->header->packageCount));
4204 return (mError=BAD_TYPE);
4205 }
4206
4207 if (parsePackage(
4208 (ResTable_package*)chunk, header, appAsLib, isSystemAsset) != NO_ERROR) {
4209 return mError;
4210 }
4211 curPackage++;
4212 } else {
4213 ALOGW("Unknown chunk type 0x%x in table at %p.\n",
4214 ctype,
4215 (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)));
4216 }
4217 chunk = (const ResChunk_header*)
4218 (((const uint8_t*)chunk) + csize);
4219 }
4220
4221 if (curPackage < dtohl(header->header->packageCount)) {
4222 ALOGW("Fewer package chunks (%d) were found than the %d declared in the header.",
4223 (int)curPackage, dtohl(header->header->packageCount));
4224 return (mError=BAD_TYPE);
4225 }
4226 mError = header->values.getError();
4227 if (mError != NO_ERROR) {
4228 ALOGW("No string values found in resource table!");
4229 }
4230
4231 if (kDebugTableNoisy) {
4232 ALOGV("Returning from add with mError=%d\n", mError);
4233 }
4234 return mError;
4235 }
4236
getError() const4237 status_t ResTable::getError() const
4238 {
4239 return mError;
4240 }
4241
uninit()4242 void ResTable::uninit()
4243 {
4244 mError = NO_INIT;
4245 size_t N = mPackageGroups.size();
4246 for (size_t i=0; i<N; i++) {
4247 PackageGroup* g = mPackageGroups[i];
4248 delete g;
4249 }
4250 N = mHeaders.size();
4251 for (size_t i=0; i<N; i++) {
4252 Header* header = mHeaders[i];
4253 if (header->owner == this) {
4254 if (header->ownedData) {
4255 free(header->ownedData);
4256 }
4257 delete header;
4258 }
4259 }
4260
4261 mPackageGroups.clear();
4262 mHeaders.clear();
4263 }
4264
getResourceName(uint32_t resID,bool allowUtf8,resource_name * outName) const4265 bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* outName) const
4266 {
4267 if (mError != NO_ERROR) {
4268 return false;
4269 }
4270
4271 const ssize_t p = getResourcePackageIndex(resID);
4272 const int t = Res_GETTYPE(resID);
4273 const int e = Res_GETENTRY(resID);
4274
4275 if (p < 0) {
4276 if (Res_GETPACKAGE(resID)+1 == 0) {
4277 ALOGW("No package identifier when getting name for resource number 0x%08x", resID);
4278 } else {
4279 #ifndef STATIC_ANDROIDFW_FOR_TOOLS
4280 ALOGW("No known package when getting name for resource number 0x%08x", resID);
4281 #endif
4282 }
4283 return false;
4284 }
4285 if (t < 0) {
4286 ALOGW("No type identifier when getting name for resource number 0x%08x", resID);
4287 return false;
4288 }
4289
4290 const PackageGroup* const grp = mPackageGroups[p];
4291 if (grp == NULL) {
4292 ALOGW("Bad identifier when getting name for resource number 0x%08x", resID);
4293 return false;
4294 }
4295
4296 Entry entry;
4297 status_t err = getEntry(grp, t, e, NULL, &entry);
4298 if (err != NO_ERROR) {
4299 return false;
4300 }
4301
4302 outName->package = grp->name.string();
4303 outName->packageLen = grp->name.size();
4304 if (allowUtf8) {
4305 outName->type8 = entry.typeStr.string8(&outName->typeLen);
4306 outName->name8 = entry.keyStr.string8(&outName->nameLen);
4307 } else {
4308 outName->type8 = NULL;
4309 outName->name8 = NULL;
4310 }
4311 if (outName->type8 == NULL) {
4312 outName->type = entry.typeStr.string16(&outName->typeLen);
4313 // If we have a bad index for some reason, we should abort.
4314 if (outName->type == NULL) {
4315 return false;
4316 }
4317 }
4318 if (outName->name8 == NULL) {
4319 outName->name = entry.keyStr.string16(&outName->nameLen);
4320 // If we have a bad index for some reason, we should abort.
4321 if (outName->name == NULL) {
4322 return false;
4323 }
4324 }
4325
4326 return true;
4327 }
4328
getResource(uint32_t resID,Res_value * outValue,bool mayBeBag,uint16_t density,uint32_t * outSpecFlags,ResTable_config * outConfig) const4329 ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density,
4330 uint32_t* outSpecFlags, ResTable_config* outConfig) const
4331 {
4332 if (mError != NO_ERROR) {
4333 return mError;
4334 }
4335
4336 const ssize_t p = getResourcePackageIndex(resID);
4337 const int t = Res_GETTYPE(resID);
4338 const int e = Res_GETENTRY(resID);
4339
4340 if (p < 0) {
4341 if (Res_GETPACKAGE(resID)+1 == 0) {
4342 ALOGW("No package identifier when getting value for resource number 0x%08x", resID);
4343 } else {
4344 ALOGW("No known package when getting value for resource number 0x%08x", resID);
4345 }
4346 return BAD_INDEX;
4347 }
4348 if (t < 0) {
4349 ALOGW("No type identifier when getting value for resource number 0x%08x", resID);
4350 return BAD_INDEX;
4351 }
4352
4353 const PackageGroup* const grp = mPackageGroups[p];
4354 if (grp == NULL) {
4355 ALOGW("Bad identifier when getting value for resource number 0x%08x", resID);
4356 return BAD_INDEX;
4357 }
4358
4359 // Allow overriding density
4360 ResTable_config desiredConfig = mParams;
4361 if (density > 0) {
4362 desiredConfig.density = density;
4363 }
4364
4365 Entry entry;
4366 status_t err = getEntry(grp, t, e, &desiredConfig, &entry);
4367 if (err != NO_ERROR) {
4368 // Only log the failure when we're not running on the host as
4369 // part of a tool. The caller will do its own logging.
4370 #ifndef STATIC_ANDROIDFW_FOR_TOOLS
4371 ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) (error %d)\n",
4372 resID, t, e, err);
4373 #endif
4374 return err;
4375 }
4376
4377 if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) {
4378 if (!mayBeBag) {
4379 ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID);
4380 }
4381 return BAD_VALUE;
4382 }
4383
4384 const Res_value* value = reinterpret_cast<const Res_value*>(
4385 reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size);
4386
4387 outValue->size = dtohs(value->size);
4388 outValue->res0 = value->res0;
4389 outValue->dataType = value->dataType;
4390 outValue->data = dtohl(value->data);
4391
4392 // The reference may be pointing to a resource in a shared library. These
4393 // references have build-time generated package IDs. These ids may not match
4394 // the actual package IDs of the corresponding packages in this ResTable.
4395 // We need to fix the package ID based on a mapping.
4396 if (grp->dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) {
4397 ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data);
4398 return BAD_VALUE;
4399 }
4400
4401 if (kDebugTableNoisy) {
4402 size_t len;
4403 printf("Found value: pkg=%zu, type=%d, str=%s, int=%d\n",
4404 entry.package->header->index,
4405 outValue->dataType,
4406 outValue->dataType == Res_value::TYPE_STRING ?
4407 String8(entry.package->header->values.stringAt(outValue->data, &len)).string() :
4408 "",
4409 outValue->data);
4410 }
4411
4412 if (outSpecFlags != NULL) {
4413 *outSpecFlags = entry.specFlags;
4414 }
4415
4416 if (outConfig != NULL) {
4417 *outConfig = entry.config;
4418 }
4419
4420 return entry.package->header->index;
4421 }
4422
resolveReference(Res_value * value,ssize_t blockIndex,uint32_t * outLastRef,uint32_t * inoutTypeSpecFlags,ResTable_config * outConfig) const4423 ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
4424 uint32_t* outLastRef, uint32_t* inoutTypeSpecFlags,
4425 ResTable_config* outConfig) const
4426 {
4427 int count=0;
4428 while (blockIndex >= 0 && value->dataType == Res_value::TYPE_REFERENCE
4429 && value->data != 0 && count < 20) {
4430 if (outLastRef) *outLastRef = value->data;
4431 uint32_t newFlags = 0;
4432 const ssize_t newIndex = getResource(value->data, value, true, 0, &newFlags,
4433 outConfig);
4434 if (newIndex == BAD_INDEX) {
4435 return BAD_INDEX;
4436 }
4437 if (kDebugTableTheme) {
4438 ALOGI("Resolving reference 0x%x: newIndex=%d, type=0x%x, data=0x%x\n",
4439 value->data, (int)newIndex, (int)value->dataType, value->data);
4440 }
4441 //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex);
4442 if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags;
4443 if (newIndex < 0) {
4444 // This can fail if the resource being referenced is a style...
4445 // in this case, just return the reference, and expect the
4446 // caller to deal with.
4447 return blockIndex;
4448 }
4449 blockIndex = newIndex;
4450 count++;
4451 }
4452 return blockIndex;
4453 }
4454
valueToString(const Res_value * value,size_t stringBlock,char16_t[TMP_BUFFER_SIZE],size_t * outLen) const4455 const char16_t* ResTable::valueToString(
4456 const Res_value* value, size_t stringBlock,
4457 char16_t /*tmpBuffer*/ [TMP_BUFFER_SIZE], size_t* outLen) const
4458 {
4459 if (!value) {
4460 return NULL;
4461 }
4462 if (value->dataType == value->TYPE_STRING) {
4463 return getTableStringBlock(stringBlock)->stringAt(value->data, outLen);
4464 }
4465 // XXX do int to string conversions.
4466 return NULL;
4467 }
4468
lockBag(uint32_t resID,const bag_entry ** outBag) const4469 ssize_t ResTable::lockBag(uint32_t resID, const bag_entry** outBag) const
4470 {
4471 mLock.lock();
4472 ssize_t err = getBagLocked(resID, outBag);
4473 if (err < NO_ERROR) {
4474 //printf("*** get failed! unlocking\n");
4475 mLock.unlock();
4476 }
4477 return err;
4478 }
4479
unlockBag(const bag_entry *) const4480 void ResTable::unlockBag(const bag_entry* /*bag*/) const
4481 {
4482 //printf("<<< unlockBag %p\n", this);
4483 mLock.unlock();
4484 }
4485
lock() const4486 void ResTable::lock() const
4487 {
4488 mLock.lock();
4489 }
4490
unlock() const4491 void ResTable::unlock() const
4492 {
4493 mLock.unlock();
4494 }
4495
getBagLocked(uint32_t resID,const bag_entry ** outBag,uint32_t * outTypeSpecFlags) const4496 ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
4497 uint32_t* outTypeSpecFlags) const
4498 {
4499 if (mError != NO_ERROR) {
4500 return mError;
4501 }
4502
4503 const ssize_t p = getResourcePackageIndex(resID);
4504 const int t = Res_GETTYPE(resID);
4505 const int e = Res_GETENTRY(resID);
4506
4507 if (p < 0) {
4508 ALOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID);
4509 return BAD_INDEX;
4510 }
4511 if (t < 0) {
4512 ALOGW("No type identifier when getting bag for resource number 0x%08x", resID);
4513 return BAD_INDEX;
4514 }
4515
4516 //printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t);
4517 PackageGroup* const grp = mPackageGroups[p];
4518 if (grp == NULL) {
4519 ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID);
4520 return BAD_INDEX;
4521 }
4522
4523 const TypeList& typeConfigs = grp->types[t];
4524 if (typeConfigs.isEmpty()) {
4525 ALOGW("Type identifier 0x%x does not exist.", t+1);
4526 return BAD_INDEX;
4527 }
4528
4529 const size_t NENTRY = typeConfigs[0]->entryCount;
4530 if (e >= (int)NENTRY) {
4531 ALOGW("Entry identifier 0x%x is larger than entry count 0x%x",
4532 e, (int)typeConfigs[0]->entryCount);
4533 return BAD_INDEX;
4534 }
4535
4536 // First see if we've already computed this bag...
4537 TypeCacheEntry& cacheEntry = grp->typeCacheEntries.editItemAt(t);
4538 bag_set** typeSet = cacheEntry.cachedBags;
4539 if (typeSet) {
4540 bag_set* set = typeSet[e];
4541 if (set) {
4542 if (set != (bag_set*)0xFFFFFFFF) {
4543 if (outTypeSpecFlags != NULL) {
4544 *outTypeSpecFlags = set->typeSpecFlags;
4545 }
4546 *outBag = (bag_entry*)(set+1);
4547 if (kDebugTableSuperNoisy) {
4548 ALOGI("Found existing bag for: 0x%x\n", resID);
4549 }
4550 return set->numAttrs;
4551 }
4552 ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
4553 resID);
4554 return BAD_INDEX;
4555 }
4556 }
4557
4558 // Bag not found, we need to compute it!
4559 if (!typeSet) {
4560 typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*));
4561 if (!typeSet) return NO_MEMORY;
4562 cacheEntry.cachedBags = typeSet;
4563 }
4564
4565 // Mark that we are currently working on this one.
4566 typeSet[e] = (bag_set*)0xFFFFFFFF;
4567
4568 if (kDebugTableNoisy) {
4569 ALOGI("Building bag: %x\n", resID);
4570 }
4571
4572 // Now collect all bag attributes
4573 Entry entry;
4574 status_t err = getEntry(grp, t, e, &mParams, &entry);
4575 if (err != NO_ERROR) {
4576 return err;
4577 }
4578
4579 const uint16_t entrySize = dtohs(entry.entry->size);
4580 const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
4581 ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0;
4582 const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
4583 ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0;
4584
4585 size_t N = count;
4586
4587 if (kDebugTableNoisy) {
4588 ALOGI("Found map: size=%x parent=%x count=%d\n", entrySize, parent, count);
4589
4590 // If this map inherits from another, we need to start
4591 // with its parent's values. Otherwise start out empty.
4592 ALOGI("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", entrySize, parent);
4593 }
4594
4595 // This is what we are building.
4596 bag_set* set = NULL;
4597
4598 if (parent) {
4599 uint32_t resolvedParent = parent;
4600
4601 // Bags encode a parent reference without using the standard
4602 // Res_value structure. That means we must always try to
4603 // resolve a parent reference in case it is actually a
4604 // TYPE_DYNAMIC_REFERENCE.
4605 status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent);
4606 if (err != NO_ERROR) {
4607 ALOGE("Failed resolving bag parent id 0x%08x", parent);
4608 return UNKNOWN_ERROR;
4609 }
4610
4611 const bag_entry* parentBag;
4612 uint32_t parentTypeSpecFlags = 0;
4613 const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags);
4614 const size_t NT = ((NP >= 0) ? NP : 0) + N;
4615 set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
4616 if (set == NULL) {
4617 return NO_MEMORY;
4618 }
4619 if (NP > 0) {
4620 memcpy(set+1, parentBag, NP*sizeof(bag_entry));
4621 set->numAttrs = NP;
4622 if (kDebugTableNoisy) {
4623 ALOGI("Initialized new bag with %zd inherited attributes.\n", NP);
4624 }
4625 } else {
4626 if (kDebugTableNoisy) {
4627 ALOGI("Initialized new bag with no inherited attributes.\n");
4628 }
4629 set->numAttrs = 0;
4630 }
4631 set->availAttrs = NT;
4632 set->typeSpecFlags = parentTypeSpecFlags;
4633 } else {
4634 set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
4635 if (set == NULL) {
4636 return NO_MEMORY;
4637 }
4638 set->numAttrs = 0;
4639 set->availAttrs = N;
4640 set->typeSpecFlags = 0;
4641 }
4642
4643 set->typeSpecFlags |= entry.specFlags;
4644
4645 // Now merge in the new attributes...
4646 size_t curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type))
4647 + dtohs(entry.entry->size);
4648 const ResTable_map* map;
4649 bag_entry* entries = (bag_entry*)(set+1);
4650 size_t curEntry = 0;
4651 uint32_t pos = 0;
4652 if (kDebugTableNoisy) {
4653 ALOGI("Starting with set %p, entries=%p, avail=%zu\n", set, entries, set->availAttrs);
4654 }
4655 while (pos < count) {
4656 if (kDebugTableNoisy) {
4657 ALOGI("Now at %p\n", (void*)curOff);
4658 }
4659
4660 if (curOff > (dtohl(entry.type->header.size)-sizeof(ResTable_map))) {
4661 ALOGW("ResTable_map at %d is beyond type chunk data %d",
4662 (int)curOff, dtohl(entry.type->header.size));
4663 free(set);
4664 return BAD_TYPE;
4665 }
4666 map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff);
4667 N++;
4668
4669 uint32_t newName = htodl(map->name.ident);
4670 if (!Res_INTERNALID(newName)) {
4671 // Attributes don't have a resource id as the name. They specify
4672 // other data, which would be wrong to change via a lookup.
4673 if (grp->dynamicRefTable.lookupResourceId(&newName) != NO_ERROR) {
4674 ALOGE("Failed resolving ResTable_map name at %d with ident 0x%08x",
4675 (int) curOff, (int) newName);
4676 free(set);
4677 return UNKNOWN_ERROR;
4678 }
4679 }
4680
4681 bool isInside;
4682 uint32_t oldName = 0;
4683 while ((isInside=(curEntry < set->numAttrs))
4684 && (oldName=entries[curEntry].map.name.ident) < newName) {
4685 if (kDebugTableNoisy) {
4686 ALOGI("#%zu: Keeping existing attribute: 0x%08x\n",
4687 curEntry, entries[curEntry].map.name.ident);
4688 }
4689 curEntry++;
4690 }
4691
4692 if ((!isInside) || oldName != newName) {
4693 // This is a new attribute... figure out what to do with it.
4694 if (set->numAttrs >= set->availAttrs) {
4695 // Need to alloc more memory...
4696 const size_t newAvail = set->availAttrs+N;
4697 void *oldSet = set;
4698 set = (bag_set*)realloc(set,
4699 sizeof(bag_set)
4700 + sizeof(bag_entry)*newAvail);
4701 if (set == NULL) {
4702 free(oldSet);
4703 return NO_MEMORY;
4704 }
4705 set->availAttrs = newAvail;
4706 entries = (bag_entry*)(set+1);
4707 if (kDebugTableNoisy) {
4708 ALOGI("Reallocated set %p, entries=%p, avail=%zu\n",
4709 set, entries, set->availAttrs);
4710 }
4711 }
4712 if (isInside) {
4713 // Going in the middle, need to make space.
4714 memmove(entries+curEntry+1, entries+curEntry,
4715 sizeof(bag_entry)*(set->numAttrs-curEntry));
4716 set->numAttrs++;
4717 }
4718 if (kDebugTableNoisy) {
4719 ALOGI("#%zu: Inserting new attribute: 0x%08x\n", curEntry, newName);
4720 }
4721 } else {
4722 if (kDebugTableNoisy) {
4723 ALOGI("#%zu: Replacing existing attribute: 0x%08x\n", curEntry, oldName);
4724 }
4725 }
4726
4727 bag_entry* cur = entries+curEntry;
4728
4729 cur->stringBlock = entry.package->header->index;
4730 cur->map.name.ident = newName;
4731 cur->map.value.copyFrom_dtoh(map->value);
4732 status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value);
4733 if (err != NO_ERROR) {
4734 ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data);
4735 return UNKNOWN_ERROR;
4736 }
4737
4738 if (kDebugTableNoisy) {
4739 ALOGI("Setting entry #%zu %p: block=%zd, name=0x%08d, type=%d, data=0x%08x\n",
4740 curEntry, cur, cur->stringBlock, cur->map.name.ident,
4741 cur->map.value.dataType, cur->map.value.data);
4742 }
4743
4744 // On to the next!
4745 curEntry++;
4746 pos++;
4747 const size_t size = dtohs(map->value.size);
4748 curOff += size + sizeof(*map)-sizeof(map->value);
4749 }
4750
4751 if (curEntry > set->numAttrs) {
4752 set->numAttrs = curEntry;
4753 }
4754
4755 // And this is it...
4756 typeSet[e] = set;
4757 if (set) {
4758 if (outTypeSpecFlags != NULL) {
4759 *outTypeSpecFlags = set->typeSpecFlags;
4760 }
4761 *outBag = (bag_entry*)(set+1);
4762 if (kDebugTableNoisy) {
4763 ALOGI("Returning %zu attrs\n", set->numAttrs);
4764 }
4765 return set->numAttrs;
4766 }
4767 return BAD_INDEX;
4768 }
4769
setParameters(const ResTable_config * params)4770 void ResTable::setParameters(const ResTable_config* params)
4771 {
4772 AutoMutex _lock(mLock);
4773 AutoMutex _lock2(mFilteredConfigLock);
4774
4775 if (kDebugTableGetEntry) {
4776 ALOGI("Setting parameters: %s\n", params->toString().string());
4777 }
4778 mParams = *params;
4779 for (size_t p = 0; p < mPackageGroups.size(); p++) {
4780 PackageGroup* packageGroup = mPackageGroups.editItemAt(p);
4781 if (kDebugTableNoisy) {
4782 ALOGI("CLEARING BAGS FOR GROUP %zu!", p);
4783 }
4784 packageGroup->clearBagCache();
4785
4786 // Find which configurations match the set of parameters. This allows for a much
4787 // faster lookup in getEntry() if the set of values is narrowed down.
4788 for (size_t t = 0; t < packageGroup->types.size(); t++) {
4789 if (packageGroup->types[t].isEmpty()) {
4790 continue;
4791 }
4792
4793 TypeList& typeList = packageGroup->types.editItemAt(t);
4794
4795 // Retrieve the cache entry for this type.
4796 TypeCacheEntry& cacheEntry = packageGroup->typeCacheEntries.editItemAt(t);
4797
4798 for (size_t ts = 0; ts < typeList.size(); ts++) {
4799 Type* type = typeList.editItemAt(ts);
4800
4801 std::shared_ptr<Vector<const ResTable_type*>> newFilteredConfigs =
4802 std::make_shared<Vector<const ResTable_type*>>();
4803
4804 for (size_t ti = 0; ti < type->configs.size(); ti++) {
4805 ResTable_config config;
4806 config.copyFromDtoH(type->configs[ti]->config);
4807
4808 if (config.match(mParams)) {
4809 newFilteredConfigs->add(type->configs[ti]);
4810 }
4811 }
4812
4813 if (kDebugTableNoisy) {
4814 ALOGD("Updating pkg=%zu type=%zu with %zu filtered configs",
4815 p, t, newFilteredConfigs->size());
4816 }
4817
4818 cacheEntry.filteredConfigs.add(newFilteredConfigs);
4819 }
4820 }
4821 }
4822 }
4823
getParameters(ResTable_config * params) const4824 void ResTable::getParameters(ResTable_config* params) const
4825 {
4826 mLock.lock();
4827 *params = mParams;
4828 mLock.unlock();
4829 }
4830
4831 struct id_name_map {
4832 uint32_t id;
4833 size_t len;
4834 char16_t name[6];
4835 };
4836
4837 const static id_name_map ID_NAMES[] = {
4838 { ResTable_map::ATTR_TYPE, 5, { '^', 't', 'y', 'p', 'e' } },
4839 { ResTable_map::ATTR_L10N, 5, { '^', 'l', '1', '0', 'n' } },
4840 { ResTable_map::ATTR_MIN, 4, { '^', 'm', 'i', 'n' } },
4841 { ResTable_map::ATTR_MAX, 4, { '^', 'm', 'a', 'x' } },
4842 { ResTable_map::ATTR_OTHER, 6, { '^', 'o', 't', 'h', 'e', 'r' } },
4843 { ResTable_map::ATTR_ZERO, 5, { '^', 'z', 'e', 'r', 'o' } },
4844 { ResTable_map::ATTR_ONE, 4, { '^', 'o', 'n', 'e' } },
4845 { ResTable_map::ATTR_TWO, 4, { '^', 't', 'w', 'o' } },
4846 { ResTable_map::ATTR_FEW, 4, { '^', 'f', 'e', 'w' } },
4847 { ResTable_map::ATTR_MANY, 5, { '^', 'm', 'a', 'n', 'y' } },
4848 };
4849
identifierForName(const char16_t * name,size_t nameLen,const char16_t * type,size_t typeLen,const char16_t * package,size_t packageLen,uint32_t * outTypeSpecFlags) const4850 uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen,
4851 const char16_t* type, size_t typeLen,
4852 const char16_t* package,
4853 size_t packageLen,
4854 uint32_t* outTypeSpecFlags) const
4855 {
4856 if (kDebugTableSuperNoisy) {
4857 printf("Identifier for name: error=%d\n", mError);
4858 }
4859
4860 // Check for internal resource identifier as the very first thing, so
4861 // that we will always find them even when there are no resources.
4862 if (name[0] == '^') {
4863 const int N = (sizeof(ID_NAMES)/sizeof(ID_NAMES[0]));
4864 size_t len;
4865 for (int i=0; i<N; i++) {
4866 const id_name_map* m = ID_NAMES + i;
4867 len = m->len;
4868 if (len != nameLen) {
4869 continue;
4870 }
4871 for (size_t j=1; j<len; j++) {
4872 if (m->name[j] != name[j]) {
4873 goto nope;
4874 }
4875 }
4876 if (outTypeSpecFlags) {
4877 *outTypeSpecFlags = ResTable_typeSpec::SPEC_PUBLIC;
4878 }
4879 return m->id;
4880 nope:
4881 ;
4882 }
4883 if (nameLen > 7) {
4884 if (name[1] == 'i' && name[2] == 'n'
4885 && name[3] == 'd' && name[4] == 'e' && name[5] == 'x'
4886 && name[6] == '_') {
4887 int index = atoi(String8(name + 7, nameLen - 7).string());
4888 if (Res_CHECKID(index)) {
4889 ALOGW("Array resource index: %d is too large.",
4890 index);
4891 return 0;
4892 }
4893 if (outTypeSpecFlags) {
4894 *outTypeSpecFlags = ResTable_typeSpec::SPEC_PUBLIC;
4895 }
4896 return Res_MAKEARRAY(index);
4897 }
4898 }
4899 return 0;
4900 }
4901
4902 if (mError != NO_ERROR) {
4903 return 0;
4904 }
4905
4906 bool fakePublic = false;
4907
4908 // Figure out the package and type we are looking in...
4909
4910 const char16_t* packageEnd = NULL;
4911 const char16_t* typeEnd = NULL;
4912 const char16_t* const nameEnd = name+nameLen;
4913 const char16_t* p = name;
4914 while (p < nameEnd) {
4915 if (*p == ':') packageEnd = p;
4916 else if (*p == '/') typeEnd = p;
4917 p++;
4918 }
4919 if (*name == '@') {
4920 name++;
4921 if (*name == '*') {
4922 fakePublic = true;
4923 name++;
4924 }
4925 }
4926 if (name >= nameEnd) {
4927 return 0;
4928 }
4929
4930 if (packageEnd) {
4931 package = name;
4932 packageLen = packageEnd-name;
4933 name = packageEnd+1;
4934 } else if (!package) {
4935 return 0;
4936 }
4937
4938 if (typeEnd) {
4939 type = name;
4940 typeLen = typeEnd-name;
4941 name = typeEnd+1;
4942 } else if (!type) {
4943 return 0;
4944 }
4945
4946 if (name >= nameEnd) {
4947 return 0;
4948 }
4949 nameLen = nameEnd-name;
4950
4951 if (kDebugTableNoisy) {
4952 printf("Looking for identifier: type=%s, name=%s, package=%s\n",
4953 String8(type, typeLen).string(),
4954 String8(name, nameLen).string(),
4955 String8(package, packageLen).string());
4956 }
4957
4958 const String16 attr("attr");
4959 const String16 attrPrivate("^attr-private");
4960
4961 const size_t NG = mPackageGroups.size();
4962 for (size_t ig=0; ig<NG; ig++) {
4963 const PackageGroup* group = mPackageGroups[ig];
4964
4965 if (strzcmp16(package, packageLen,
4966 group->name.string(), group->name.size())) {
4967 if (kDebugTableNoisy) {
4968 printf("Skipping package group: %s\n", String8(group->name).string());
4969 }
4970 continue;
4971 }
4972
4973 const size_t packageCount = group->packages.size();
4974 for (size_t pi = 0; pi < packageCount; pi++) {
4975 const char16_t* targetType = type;
4976 size_t targetTypeLen = typeLen;
4977
4978 do {
4979 ssize_t ti = group->packages[pi]->typeStrings.indexOfString(
4980 targetType, targetTypeLen);
4981 if (ti < 0) {
4982 continue;
4983 }
4984
4985 ti += group->packages[pi]->typeIdOffset;
4986
4987 const uint32_t identifier = findEntry(group, ti, name, nameLen,
4988 outTypeSpecFlags);
4989 if (identifier != 0) {
4990 if (fakePublic && outTypeSpecFlags) {
4991 *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC;
4992 }
4993 return identifier;
4994 }
4995 } while (strzcmp16(attr.string(), attr.size(), targetType, targetTypeLen) == 0
4996 && (targetType = attrPrivate.string())
4997 && (targetTypeLen = attrPrivate.size())
4998 );
4999 }
5000 }
5001 return 0;
5002 }
5003
findEntry(const PackageGroup * group,ssize_t typeIndex,const char16_t * name,size_t nameLen,uint32_t * outTypeSpecFlags) const5004 uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const char16_t* name,
5005 size_t nameLen, uint32_t* outTypeSpecFlags) const {
5006 const TypeList& typeList = group->types[typeIndex];
5007 const size_t typeCount = typeList.size();
5008 for (size_t i = 0; i < typeCount; i++) {
5009 const Type* t = typeList[i];
5010 const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen);
5011 if (ei < 0) {
5012 continue;
5013 }
5014
5015 const size_t configCount = t->configs.size();
5016 for (size_t j = 0; j < configCount; j++) {
5017 const TypeVariant tv(t->configs[j]);
5018 for (TypeVariant::iterator iter = tv.beginEntries();
5019 iter != tv.endEntries();
5020 iter++) {
5021 const ResTable_entry* entry = *iter;
5022 if (entry == NULL) {
5023 continue;
5024 }
5025
5026 if (dtohl(entry->key.index) == (size_t) ei) {
5027 uint32_t resId = Res_MAKEID(group->id - 1, typeIndex, iter.index());
5028 if (outTypeSpecFlags) {
5029 Entry result;
5030 if (getEntry(group, typeIndex, iter.index(), NULL, &result) != NO_ERROR) {
5031 ALOGW("Failed to find spec flags for 0x%08x", resId);
5032 return 0;
5033 }
5034 *outTypeSpecFlags = result.specFlags;
5035 }
5036 return resId;
5037 }
5038 }
5039 }
5040 }
5041 return 0;
5042 }
5043
expandResourceRef(const char16_t * refStr,size_t refLen,String16 * outPackage,String16 * outType,String16 * outName,const String16 * defType,const String16 * defPackage,const char ** outErrorMsg,bool * outPublicOnly)5044 bool ResTable::expandResourceRef(const char16_t* refStr, size_t refLen,
5045 String16* outPackage,
5046 String16* outType,
5047 String16* outName,
5048 const String16* defType,
5049 const String16* defPackage,
5050 const char** outErrorMsg,
5051 bool* outPublicOnly)
5052 {
5053 const char16_t* packageEnd = NULL;
5054 const char16_t* typeEnd = NULL;
5055 const char16_t* p = refStr;
5056 const char16_t* const end = p + refLen;
5057 while (p < end) {
5058 if (*p == ':') packageEnd = p;
5059 else if (*p == '/') {
5060 typeEnd = p;
5061 break;
5062 }
5063 p++;
5064 }
5065 p = refStr;
5066 if (*p == '@') p++;
5067
5068 if (outPublicOnly != NULL) {
5069 *outPublicOnly = true;
5070 }
5071 if (*p == '*') {
5072 p++;
5073 if (outPublicOnly != NULL) {
5074 *outPublicOnly = false;
5075 }
5076 }
5077
5078 if (packageEnd) {
5079 *outPackage = String16(p, packageEnd-p);
5080 p = packageEnd+1;
5081 } else {
5082 if (!defPackage) {
5083 if (outErrorMsg) {
5084 *outErrorMsg = "No resource package specified";
5085 }
5086 return false;
5087 }
5088 *outPackage = *defPackage;
5089 }
5090 if (typeEnd) {
5091 *outType = String16(p, typeEnd-p);
5092 p = typeEnd+1;
5093 } else {
5094 if (!defType) {
5095 if (outErrorMsg) {
5096 *outErrorMsg = "No resource type specified";
5097 }
5098 return false;
5099 }
5100 *outType = *defType;
5101 }
5102 *outName = String16(p, end-p);
5103 if(**outPackage == 0) {
5104 if(outErrorMsg) {
5105 *outErrorMsg = "Resource package cannot be an empty string";
5106 }
5107 return false;
5108 }
5109 if(**outType == 0) {
5110 if(outErrorMsg) {
5111 *outErrorMsg = "Resource type cannot be an empty string";
5112 }
5113 return false;
5114 }
5115 if(**outName == 0) {
5116 if(outErrorMsg) {
5117 *outErrorMsg = "Resource id cannot be an empty string";
5118 }
5119 return false;
5120 }
5121 return true;
5122 }
5123
get_hex(char c,bool * outError)5124 static uint32_t get_hex(char c, bool* outError)
5125 {
5126 if (c >= '0' && c <= '9') {
5127 return c - '0';
5128 } else if (c >= 'a' && c <= 'f') {
5129 return c - 'a' + 0xa;
5130 } else if (c >= 'A' && c <= 'F') {
5131 return c - 'A' + 0xa;
5132 }
5133 *outError = true;
5134 return 0;
5135 }
5136
5137 struct unit_entry
5138 {
5139 const char* name;
5140 size_t len;
5141 uint8_t type;
5142 uint32_t unit;
5143 float scale;
5144 };
5145
5146 static const unit_entry unitNames[] = {
5147 { "px", strlen("px"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PX, 1.0f },
5148 { "dip", strlen("dip"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f },
5149 { "dp", strlen("dp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f },
5150 { "sp", strlen("sp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_SP, 1.0f },
5151 { "pt", strlen("pt"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PT, 1.0f },
5152 { "in", strlen("in"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_IN, 1.0f },
5153 { "mm", strlen("mm"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_MM, 1.0f },
5154 { "%", strlen("%"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION, 1.0f/100 },
5155 { "%p", strlen("%p"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100 },
5156 { NULL, 0, 0, 0, 0 }
5157 };
5158
parse_unit(const char * str,Res_value * outValue,float * outScale,const char ** outEnd)5159 static bool parse_unit(const char* str, Res_value* outValue,
5160 float* outScale, const char** outEnd)
5161 {
5162 const char* end = str;
5163 while (*end != 0 && !isspace((unsigned char)*end)) {
5164 end++;
5165 }
5166 const size_t len = end-str;
5167
5168 const char* realEnd = end;
5169 while (*realEnd != 0 && isspace((unsigned char)*realEnd)) {
5170 realEnd++;
5171 }
5172 if (*realEnd != 0) {
5173 return false;
5174 }
5175
5176 const unit_entry* cur = unitNames;
5177 while (cur->name) {
5178 if (len == cur->len && strncmp(cur->name, str, len) == 0) {
5179 outValue->dataType = cur->type;
5180 outValue->data = cur->unit << Res_value::COMPLEX_UNIT_SHIFT;
5181 *outScale = cur->scale;
5182 *outEnd = end;
5183 //printf("Found unit %s for %s\n", cur->name, str);
5184 return true;
5185 }
5186 cur++;
5187 }
5188
5189 return false;
5190 }
5191
U16StringToInt(const char16_t * s,size_t len,Res_value * outValue)5192 bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue)
5193 {
5194 while (len > 0 && isspace16(*s)) {
5195 s++;
5196 len--;
5197 }
5198
5199 if (len <= 0) {
5200 return false;
5201 }
5202
5203 size_t i = 0;
5204 int64_t val = 0;
5205 bool neg = false;
5206
5207 if (*s == '-') {
5208 neg = true;
5209 i++;
5210 }
5211
5212 if (s[i] < '0' || s[i] > '9') {
5213 return false;
5214 }
5215
5216 static_assert(std::is_same<uint32_t, Res_value::data_type>::value,
5217 "Res_value::data_type has changed. The range checks in this "
5218 "function are no longer correct.");
5219
5220 // Decimal or hex?
5221 bool isHex;
5222 if (len > 1 && s[i] == '0' && s[i+1] == 'x') {
5223 isHex = true;
5224 i += 2;
5225
5226 if (neg) {
5227 return false;
5228 }
5229
5230 if (i == len) {
5231 // Just u"0x"
5232 return false;
5233 }
5234
5235 bool error = false;
5236 while (i < len && !error) {
5237 val = (val*16) + get_hex(s[i], &error);
5238 i++;
5239
5240 if (val > std::numeric_limits<uint32_t>::max()) {
5241 return false;
5242 }
5243 }
5244 if (error) {
5245 return false;
5246 }
5247 } else {
5248 isHex = false;
5249 while (i < len) {
5250 if (s[i] < '0' || s[i] > '9') {
5251 return false;
5252 }
5253 val = (val*10) + s[i]-'0';
5254 i++;
5255
5256 if ((neg && -val < std::numeric_limits<int32_t>::min()) ||
5257 (!neg && val > std::numeric_limits<int32_t>::max())) {
5258 return false;
5259 }
5260 }
5261 }
5262
5263 if (neg) val = -val;
5264
5265 while (i < len && isspace16(s[i])) {
5266 i++;
5267 }
5268
5269 if (i != len) {
5270 return false;
5271 }
5272
5273 if (outValue) {
5274 outValue->dataType =
5275 isHex ? outValue->TYPE_INT_HEX : outValue->TYPE_INT_DEC;
5276 outValue->data = static_cast<Res_value::data_type>(val);
5277 }
5278 return true;
5279 }
5280
stringToInt(const char16_t * s,size_t len,Res_value * outValue)5281 bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue)
5282 {
5283 return U16StringToInt(s, len, outValue);
5284 }
5285
stringToFloat(const char16_t * s,size_t len,Res_value * outValue)5286 bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue)
5287 {
5288 while (len > 0 && isspace16(*s)) {
5289 s++;
5290 len--;
5291 }
5292
5293 if (len <= 0) {
5294 return false;
5295 }
5296
5297 char buf[128];
5298 int i=0;
5299 while (len > 0 && *s != 0 && i < 126) {
5300 if (*s > 255) {
5301 return false;
5302 }
5303 buf[i++] = *s++;
5304 len--;
5305 }
5306
5307 if (len > 0) {
5308 return false;
5309 }
5310 if ((buf[0] < '0' || buf[0] > '9') && buf[0] != '.' && buf[0] != '-' && buf[0] != '+') {
5311 return false;
5312 }
5313
5314 buf[i] = 0;
5315 const char* end;
5316 float f = strtof(buf, (char**)&end);
5317
5318 if (*end != 0 && !isspace((unsigned char)*end)) {
5319 // Might be a unit...
5320 float scale;
5321 if (parse_unit(end, outValue, &scale, &end)) {
5322 f *= scale;
5323 const bool neg = f < 0;
5324 if (neg) f = -f;
5325 uint64_t bits = (uint64_t)(f*(1<<23)+.5f);
5326 uint32_t radix;
5327 uint32_t shift;
5328 if ((bits&0x7fffff) == 0) {
5329 // Always use 23p0 if there is no fraction, just to make
5330 // things easier to read.
5331 radix = Res_value::COMPLEX_RADIX_23p0;
5332 shift = 23;
5333 } else if ((bits&0xffffffffff800000LL) == 0) {
5334 // Magnitude is zero -- can fit in 0 bits of precision.
5335 radix = Res_value::COMPLEX_RADIX_0p23;
5336 shift = 0;
5337 } else if ((bits&0xffffffff80000000LL) == 0) {
5338 // Magnitude can fit in 8 bits of precision.
5339 radix = Res_value::COMPLEX_RADIX_8p15;
5340 shift = 8;
5341 } else if ((bits&0xffffff8000000000LL) == 0) {
5342 // Magnitude can fit in 16 bits of precision.
5343 radix = Res_value::COMPLEX_RADIX_16p7;
5344 shift = 16;
5345 } else {
5346 // Magnitude needs entire range, so no fractional part.
5347 radix = Res_value::COMPLEX_RADIX_23p0;
5348 shift = 23;
5349 }
5350 int32_t mantissa = (int32_t)(
5351 (bits>>shift) & Res_value::COMPLEX_MANTISSA_MASK);
5352 if (neg) {
5353 mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK;
5354 }
5355 outValue->data |=
5356 (radix<<Res_value::COMPLEX_RADIX_SHIFT)
5357 | (mantissa<<Res_value::COMPLEX_MANTISSA_SHIFT);
5358 //printf("Input value: %f 0x%016Lx, mult: %f, radix: %d, shift: %d, final: 0x%08x\n",
5359 // f * (neg ? -1 : 1), bits, f*(1<<23),
5360 // radix, shift, outValue->data);
5361 return true;
5362 }
5363 return false;
5364 }
5365
5366 while (*end != 0 && isspace((unsigned char)*end)) {
5367 end++;
5368 }
5369
5370 if (*end == 0) {
5371 if (outValue) {
5372 outValue->dataType = outValue->TYPE_FLOAT;
5373 *(float*)(&outValue->data) = f;
5374 return true;
5375 }
5376 }
5377
5378 return false;
5379 }
5380
stringToValue(Res_value * outValue,String16 * outString,const char16_t * s,size_t len,bool preserveSpaces,bool coerceType,uint32_t attrID,const String16 * defType,const String16 * defPackage,Accessor * accessor,void * accessorCookie,uint32_t attrType,bool enforcePrivate) const5381 bool ResTable::stringToValue(Res_value* outValue, String16* outString,
5382 const char16_t* s, size_t len,
5383 bool preserveSpaces, bool coerceType,
5384 uint32_t attrID,
5385 const String16* defType,
5386 const String16* defPackage,
5387 Accessor* accessor,
5388 void* accessorCookie,
5389 uint32_t attrType,
5390 bool enforcePrivate) const
5391 {
5392 bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting();
5393 const char* errorMsg = NULL;
5394
5395 outValue->size = sizeof(Res_value);
5396 outValue->res0 = 0;
5397
5398 // First strip leading/trailing whitespace. Do this before handling
5399 // escapes, so they can be used to force whitespace into the string.
5400 if (!preserveSpaces) {
5401 while (len > 0 && isspace16(*s)) {
5402 s++;
5403 len--;
5404 }
5405 while (len > 0 && isspace16(s[len-1])) {
5406 len--;
5407 }
5408 // If the string ends with '\', then we keep the space after it.
5409 if (len > 0 && s[len-1] == '\\' && s[len] != 0) {
5410 len++;
5411 }
5412 }
5413
5414 //printf("Value for: %s\n", String8(s, len).string());
5415
5416 uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED;
5417 uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff;
5418 bool fromAccessor = false;
5419 if (attrID != 0 && !Res_INTERNALID(attrID)) {
5420 const ssize_t p = getResourcePackageIndex(attrID);
5421 const bag_entry* bag;
5422 ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
5423 //printf("For attr 0x%08x got bag of %d\n", attrID, cnt);
5424 if (cnt >= 0) {
5425 while (cnt > 0) {
5426 //printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data);
5427 switch (bag->map.name.ident) {
5428 case ResTable_map::ATTR_TYPE:
5429 attrType = bag->map.value.data;
5430 break;
5431 case ResTable_map::ATTR_MIN:
5432 attrMin = bag->map.value.data;
5433 break;
5434 case ResTable_map::ATTR_MAX:
5435 attrMax = bag->map.value.data;
5436 break;
5437 case ResTable_map::ATTR_L10N:
5438 l10nReq = bag->map.value.data;
5439 break;
5440 }
5441 bag++;
5442 cnt--;
5443 }
5444 unlockBag(bag);
5445 } else if (accessor && accessor->getAttributeType(attrID, &attrType)) {
5446 fromAccessor = true;
5447 if (attrType == ResTable_map::TYPE_ENUM
5448 || attrType == ResTable_map::TYPE_FLAGS
5449 || attrType == ResTable_map::TYPE_INTEGER) {
5450 accessor->getAttributeMin(attrID, &attrMin);
5451 accessor->getAttributeMax(attrID, &attrMax);
5452 }
5453 if (localizationSetting) {
5454 l10nReq = accessor->getAttributeL10N(attrID);
5455 }
5456 }
5457 }
5458
5459 const bool canStringCoerce =
5460 coerceType && (attrType&ResTable_map::TYPE_STRING) != 0;
5461
5462 if (*s == '@') {
5463 outValue->dataType = outValue->TYPE_REFERENCE;
5464
5465 // Note: we don't check attrType here because the reference can
5466 // be to any other type; we just need to count on the client making
5467 // sure the referenced type is correct.
5468
5469 //printf("Looking up ref: %s\n", String8(s, len).string());
5470
5471 // It's a reference!
5472 if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') {
5473 // Special case @null as undefined. This will be converted by
5474 // AssetManager to TYPE_NULL with data DATA_NULL_UNDEFINED.
5475 outValue->data = 0;
5476 return true;
5477 } else if (len == 6 && s[1]=='e' && s[2]=='m' && s[3]=='p' && s[4]=='t' && s[5]=='y') {
5478 // Special case @empty as explicitly defined empty value.
5479 outValue->dataType = Res_value::TYPE_NULL;
5480 outValue->data = Res_value::DATA_NULL_EMPTY;
5481 return true;
5482 } else {
5483 bool createIfNotFound = false;
5484 const char16_t* resourceRefName;
5485 int resourceNameLen;
5486 if (len > 2 && s[1] == '+') {
5487 createIfNotFound = true;
5488 resourceRefName = s + 2;
5489 resourceNameLen = len - 2;
5490 } else if (len > 2 && s[1] == '*') {
5491 enforcePrivate = false;
5492 resourceRefName = s + 2;
5493 resourceNameLen = len - 2;
5494 } else {
5495 createIfNotFound = false;
5496 resourceRefName = s + 1;
5497 resourceNameLen = len - 1;
5498 }
5499 String16 package, type, name;
5500 if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name,
5501 defType, defPackage, &errorMsg)) {
5502 if (accessor != NULL) {
5503 accessor->reportError(accessorCookie, errorMsg);
5504 }
5505 return false;
5506 }
5507
5508 uint32_t specFlags = 0;
5509 uint32_t rid = identifierForName(name.string(), name.size(), type.string(),
5510 type.size(), package.string(), package.size(), &specFlags);
5511 if (rid != 0) {
5512 if (enforcePrivate) {
5513 if (accessor == NULL || accessor->getAssetsPackage() != package) {
5514 if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
5515 if (accessor != NULL) {
5516 accessor->reportError(accessorCookie, "Resource is not public.");
5517 }
5518 return false;
5519 }
5520 }
5521 }
5522
5523 if (accessor) {
5524 rid = Res_MAKEID(
5525 accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
5526 Res_GETTYPE(rid), Res_GETENTRY(rid));
5527 if (kDebugTableNoisy) {
5528 ALOGI("Incl %s:%s/%s: 0x%08x\n",
5529 String8(package).string(), String8(type).string(),
5530 String8(name).string(), rid);
5531 }
5532 }
5533
5534 uint32_t packageId = Res_GETPACKAGE(rid) + 1;
5535 if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) {
5536 outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
5537 }
5538 outValue->data = rid;
5539 return true;
5540 }
5541
5542 if (accessor) {
5543 uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name,
5544 createIfNotFound);
5545 if (rid != 0) {
5546 if (kDebugTableNoisy) {
5547 ALOGI("Pckg %s:%s/%s: 0x%08x\n",
5548 String8(package).string(), String8(type).string(),
5549 String8(name).string(), rid);
5550 }
5551 uint32_t packageId = Res_GETPACKAGE(rid) + 1;
5552 if (packageId == 0x00) {
5553 outValue->data = rid;
5554 outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
5555 return true;
5556 } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) {
5557 // We accept packageId's generated as 0x01 in order to support
5558 // building the android system resources
5559 outValue->data = rid;
5560 return true;
5561 }
5562 }
5563 }
5564 }
5565
5566 if (accessor != NULL) {
5567 accessor->reportError(accessorCookie, "No resource found that matches the given name");
5568 }
5569 return false;
5570 }
5571
5572 // if we got to here, and localization is required and it's not a reference,
5573 // complain and bail.
5574 if (l10nReq == ResTable_map::L10N_SUGGESTED) {
5575 if (localizationSetting) {
5576 if (accessor != NULL) {
5577 accessor->reportError(accessorCookie, "This attribute must be localized.");
5578 }
5579 }
5580 }
5581
5582 if (*s == '#') {
5583 // It's a color! Convert to an integer of the form 0xaarrggbb.
5584 uint32_t color = 0;
5585 bool error = false;
5586 if (len == 4) {
5587 outValue->dataType = outValue->TYPE_INT_COLOR_RGB4;
5588 color |= 0xFF000000;
5589 color |= get_hex(s[1], &error) << 20;
5590 color |= get_hex(s[1], &error) << 16;
5591 color |= get_hex(s[2], &error) << 12;
5592 color |= get_hex(s[2], &error) << 8;
5593 color |= get_hex(s[3], &error) << 4;
5594 color |= get_hex(s[3], &error);
5595 } else if (len == 5) {
5596 outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4;
5597 color |= get_hex(s[1], &error) << 28;
5598 color |= get_hex(s[1], &error) << 24;
5599 color |= get_hex(s[2], &error) << 20;
5600 color |= get_hex(s[2], &error) << 16;
5601 color |= get_hex(s[3], &error) << 12;
5602 color |= get_hex(s[3], &error) << 8;
5603 color |= get_hex(s[4], &error) << 4;
5604 color |= get_hex(s[4], &error);
5605 } else if (len == 7) {
5606 outValue->dataType = outValue->TYPE_INT_COLOR_RGB8;
5607 color |= 0xFF000000;
5608 color |= get_hex(s[1], &error) << 20;
5609 color |= get_hex(s[2], &error) << 16;
5610 color |= get_hex(s[3], &error) << 12;
5611 color |= get_hex(s[4], &error) << 8;
5612 color |= get_hex(s[5], &error) << 4;
5613 color |= get_hex(s[6], &error);
5614 } else if (len == 9) {
5615 outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8;
5616 color |= get_hex(s[1], &error) << 28;
5617 color |= get_hex(s[2], &error) << 24;
5618 color |= get_hex(s[3], &error) << 20;
5619 color |= get_hex(s[4], &error) << 16;
5620 color |= get_hex(s[5], &error) << 12;
5621 color |= get_hex(s[6], &error) << 8;
5622 color |= get_hex(s[7], &error) << 4;
5623 color |= get_hex(s[8], &error);
5624 } else {
5625 error = true;
5626 }
5627 if (!error) {
5628 if ((attrType&ResTable_map::TYPE_COLOR) == 0) {
5629 if (!canStringCoerce) {
5630 if (accessor != NULL) {
5631 accessor->reportError(accessorCookie,
5632 "Color types not allowed");
5633 }
5634 return false;
5635 }
5636 } else {
5637 outValue->data = color;
5638 //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color);
5639 return true;
5640 }
5641 } else {
5642 if ((attrType&ResTable_map::TYPE_COLOR) != 0) {
5643 if (accessor != NULL) {
5644 accessor->reportError(accessorCookie, "Color value not valid --"
5645 " must be #rgb, #argb, #rrggbb, or #aarrggbb");
5646 }
5647 #if 0
5648 fprintf(stderr, "%s: Color ID %s value %s is not valid\n",
5649 "Resource File", //(const char*)in->getPrintableSource(),
5650 String8(*curTag).string(),
5651 String8(s, len).string());
5652 #endif
5653 return false;
5654 }
5655 }
5656 }
5657
5658 if (*s == '?') {
5659 outValue->dataType = outValue->TYPE_ATTRIBUTE;
5660
5661 // Note: we don't check attrType here because the reference can
5662 // be to any other type; we just need to count on the client making
5663 // sure the referenced type is correct.
5664
5665 //printf("Looking up attr: %s\n", String8(s, len).string());
5666
5667 static const String16 attr16("attr");
5668 String16 package, type, name;
5669 if (!expandResourceRef(s+1, len-1, &package, &type, &name,
5670 &attr16, defPackage, &errorMsg)) {
5671 if (accessor != NULL) {
5672 accessor->reportError(accessorCookie, errorMsg);
5673 }
5674 return false;
5675 }
5676
5677 //printf("Pkg: %s, Type: %s, Name: %s\n",
5678 // String8(package).string(), String8(type).string(),
5679 // String8(name).string());
5680 uint32_t specFlags = 0;
5681 uint32_t rid =
5682 identifierForName(name.string(), name.size(),
5683 type.string(), type.size(),
5684 package.string(), package.size(), &specFlags);
5685 if (rid != 0) {
5686 if (enforcePrivate) {
5687 if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
5688 if (accessor != NULL) {
5689 accessor->reportError(accessorCookie, "Attribute is not public.");
5690 }
5691 return false;
5692 }
5693 }
5694
5695 if (accessor) {
5696 rid = Res_MAKEID(
5697 accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
5698 Res_GETTYPE(rid), Res_GETENTRY(rid));
5699 }
5700
5701 uint32_t packageId = Res_GETPACKAGE(rid) + 1;
5702 if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) {
5703 outValue->dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE;
5704 }
5705 outValue->data = rid;
5706 return true;
5707 }
5708
5709 if (accessor) {
5710 uint32_t rid = accessor->getCustomResource(package, type, name);
5711 if (rid != 0) {
5712 uint32_t packageId = Res_GETPACKAGE(rid) + 1;
5713 if (packageId == 0x00) {
5714 outValue->data = rid;
5715 outValue->dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE;
5716 return true;
5717 } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) {
5718 // We accept packageId's generated as 0x01 in order to support
5719 // building the android system resources
5720 outValue->data = rid;
5721 return true;
5722 }
5723 }
5724 }
5725
5726 if (accessor != NULL) {
5727 accessor->reportError(accessorCookie, "No resource found that matches the given name");
5728 }
5729 return false;
5730 }
5731
5732 if (stringToInt(s, len, outValue)) {
5733 if ((attrType&ResTable_map::TYPE_INTEGER) == 0) {
5734 // If this type does not allow integers, but does allow floats,
5735 // fall through on this error case because the float type should
5736 // be able to accept any integer value.
5737 if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) {
5738 if (accessor != NULL) {
5739 accessor->reportError(accessorCookie, "Integer types not allowed");
5740 }
5741 return false;
5742 }
5743 } else {
5744 if (((int32_t)outValue->data) < ((int32_t)attrMin)
5745 || ((int32_t)outValue->data) > ((int32_t)attrMax)) {
5746 if (accessor != NULL) {
5747 accessor->reportError(accessorCookie, "Integer value out of range");
5748 }
5749 return false;
5750 }
5751 return true;
5752 }
5753 }
5754
5755 if (stringToFloat(s, len, outValue)) {
5756 if (outValue->dataType == Res_value::TYPE_DIMENSION) {
5757 if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) {
5758 return true;
5759 }
5760 if (!canStringCoerce) {
5761 if (accessor != NULL) {
5762 accessor->reportError(accessorCookie, "Dimension types not allowed");
5763 }
5764 return false;
5765 }
5766 } else if (outValue->dataType == Res_value::TYPE_FRACTION) {
5767 if ((attrType&ResTable_map::TYPE_FRACTION) != 0) {
5768 return true;
5769 }
5770 if (!canStringCoerce) {
5771 if (accessor != NULL) {
5772 accessor->reportError(accessorCookie, "Fraction types not allowed");
5773 }
5774 return false;
5775 }
5776 } else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) {
5777 if (!canStringCoerce) {
5778 if (accessor != NULL) {
5779 accessor->reportError(accessorCookie, "Float types not allowed");
5780 }
5781 return false;
5782 }
5783 } else {
5784 return true;
5785 }
5786 }
5787
5788 if (len == 4) {
5789 if ((s[0] == 't' || s[0] == 'T') &&
5790 (s[1] == 'r' || s[1] == 'R') &&
5791 (s[2] == 'u' || s[2] == 'U') &&
5792 (s[3] == 'e' || s[3] == 'E')) {
5793 if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) {
5794 if (!canStringCoerce) {
5795 if (accessor != NULL) {
5796 accessor->reportError(accessorCookie, "Boolean types not allowed");
5797 }
5798 return false;
5799 }
5800 } else {
5801 outValue->dataType = outValue->TYPE_INT_BOOLEAN;
5802 outValue->data = (uint32_t)-1;
5803 return true;
5804 }
5805 }
5806 }
5807
5808 if (len == 5) {
5809 if ((s[0] == 'f' || s[0] == 'F') &&
5810 (s[1] == 'a' || s[1] == 'A') &&
5811 (s[2] == 'l' || s[2] == 'L') &&
5812 (s[3] == 's' || s[3] == 'S') &&
5813 (s[4] == 'e' || s[4] == 'E')) {
5814 if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) {
5815 if (!canStringCoerce) {
5816 if (accessor != NULL) {
5817 accessor->reportError(accessorCookie, "Boolean types not allowed");
5818 }
5819 return false;
5820 }
5821 } else {
5822 outValue->dataType = outValue->TYPE_INT_BOOLEAN;
5823 outValue->data = 0;
5824 return true;
5825 }
5826 }
5827 }
5828
5829 if ((attrType&ResTable_map::TYPE_ENUM) != 0) {
5830 const ssize_t p = getResourcePackageIndex(attrID);
5831 const bag_entry* bag;
5832 ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
5833 //printf("Got %d for enum\n", cnt);
5834 if (cnt >= 0) {
5835 resource_name rname;
5836 while (cnt > 0) {
5837 if (!Res_INTERNALID(bag->map.name.ident)) {
5838 //printf("Trying attr #%08x\n", bag->map.name.ident);
5839 if (getResourceName(bag->map.name.ident, false, &rname)) {
5840 #if 0
5841 printf("Matching %s against %s (0x%08x)\n",
5842 String8(s, len).string(),
5843 String8(rname.name, rname.nameLen).string(),
5844 bag->map.name.ident);
5845 #endif
5846 if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) {
5847 outValue->dataType = bag->map.value.dataType;
5848 outValue->data = bag->map.value.data;
5849 unlockBag(bag);
5850 return true;
5851 }
5852 }
5853
5854 }
5855 bag++;
5856 cnt--;
5857 }
5858 unlockBag(bag);
5859 }
5860
5861 if (fromAccessor) {
5862 if (accessor->getAttributeEnum(attrID, s, len, outValue)) {
5863 return true;
5864 }
5865 }
5866 }
5867
5868 if ((attrType&ResTable_map::TYPE_FLAGS) != 0) {
5869 const ssize_t p = getResourcePackageIndex(attrID);
5870 const bag_entry* bag;
5871 ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
5872 //printf("Got %d for flags\n", cnt);
5873 if (cnt >= 0) {
5874 bool failed = false;
5875 resource_name rname;
5876 outValue->dataType = Res_value::TYPE_INT_HEX;
5877 outValue->data = 0;
5878 const char16_t* end = s + len;
5879 const char16_t* pos = s;
5880 while (pos < end && !failed) {
5881 const char16_t* start = pos;
5882 pos++;
5883 while (pos < end && *pos != '|') {
5884 pos++;
5885 }
5886 //printf("Looking for: %s\n", String8(start, pos-start).string());
5887 const bag_entry* bagi = bag;
5888 ssize_t i;
5889 for (i=0; i<cnt; i++, bagi++) {
5890 if (!Res_INTERNALID(bagi->map.name.ident)) {
5891 //printf("Trying attr #%08x\n", bagi->map.name.ident);
5892 if (getResourceName(bagi->map.name.ident, false, &rname)) {
5893 #if 0
5894 printf("Matching %s against %s (0x%08x)\n",
5895 String8(start,pos-start).string(),
5896 String8(rname.name, rname.nameLen).string(),
5897 bagi->map.name.ident);
5898 #endif
5899 if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) {
5900 outValue->data |= bagi->map.value.data;
5901 break;
5902 }
5903 }
5904 }
5905 }
5906 if (i >= cnt) {
5907 // Didn't find this flag identifier.
5908 failed = true;
5909 }
5910 if (pos < end) {
5911 pos++;
5912 }
5913 }
5914 unlockBag(bag);
5915 if (!failed) {
5916 //printf("Final flag value: 0x%lx\n", outValue->data);
5917 return true;
5918 }
5919 }
5920
5921
5922 if (fromAccessor) {
5923 if (accessor->getAttributeFlags(attrID, s, len, outValue)) {
5924 //printf("Final flag value: 0x%lx\n", outValue->data);
5925 return true;
5926 }
5927 }
5928 }
5929
5930 if ((attrType&ResTable_map::TYPE_STRING) == 0) {
5931 if (accessor != NULL) {
5932 accessor->reportError(accessorCookie, "String types not allowed");
5933 }
5934 return false;
5935 }
5936
5937 // Generic string handling...
5938 outValue->dataType = outValue->TYPE_STRING;
5939 if (outString) {
5940 bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg);
5941 if (accessor != NULL) {
5942 accessor->reportError(accessorCookie, errorMsg);
5943 }
5944 return failed;
5945 }
5946
5947 return true;
5948 }
5949
collectString(String16 * outString,const char16_t * s,size_t len,bool preserveSpaces,const char ** outErrorMsg,bool append)5950 bool ResTable::collectString(String16* outString,
5951 const char16_t* s, size_t len,
5952 bool preserveSpaces,
5953 const char** outErrorMsg,
5954 bool append)
5955 {
5956 String16 tmp;
5957
5958 char quoted = 0;
5959 const char16_t* p = s;
5960 while (p < (s+len)) {
5961 while (p < (s+len)) {
5962 const char16_t c = *p;
5963 if (c == '\\') {
5964 break;
5965 }
5966 if (!preserveSpaces) {
5967 if (quoted == 0 && isspace16(c)
5968 && (c != ' ' || isspace16(*(p+1)))) {
5969 break;
5970 }
5971 if (c == '"' && (quoted == 0 || quoted == '"')) {
5972 break;
5973 }
5974 if (c == '\'' && (quoted == 0 || quoted == '\'')) {
5975 /*
5976 * In practice, when people write ' instead of \'
5977 * in a string, they are doing it by accident
5978 * instead of really meaning to use ' as a quoting
5979 * character. Warn them so they don't lose it.
5980 */
5981 if (outErrorMsg) {
5982 *outErrorMsg = "Apostrophe not preceded by \\";
5983 }
5984 return false;
5985 }
5986 }
5987 p++;
5988 }
5989 if (p < (s+len)) {
5990 if (p > s) {
5991 tmp.append(String16(s, p-s));
5992 }
5993 if (!preserveSpaces && (*p == '"' || *p == '\'')) {
5994 if (quoted == 0) {
5995 quoted = *p;
5996 } else {
5997 quoted = 0;
5998 }
5999 p++;
6000 } else if (!preserveSpaces && isspace16(*p)) {
6001 // Space outside of a quote -- consume all spaces and
6002 // leave a single plain space char.
6003 tmp.append(String16(" "));
6004 p++;
6005 while (p < (s+len) && isspace16(*p)) {
6006 p++;
6007 }
6008 } else if (*p == '\\') {
6009 p++;
6010 if (p < (s+len)) {
6011 switch (*p) {
6012 case 't':
6013 tmp.append(String16("\t"));
6014 break;
6015 case 'n':
6016 tmp.append(String16("\n"));
6017 break;
6018 case '#':
6019 tmp.append(String16("#"));
6020 break;
6021 case '@':
6022 tmp.append(String16("@"));
6023 break;
6024 case '?':
6025 tmp.append(String16("?"));
6026 break;
6027 case '"':
6028 tmp.append(String16("\""));
6029 break;
6030 case '\'':
6031 tmp.append(String16("'"));
6032 break;
6033 case '\\':
6034 tmp.append(String16("\\"));
6035 break;
6036 case 'u':
6037 {
6038 char16_t chr = 0;
6039 int i = 0;
6040 while (i < 4 && p[1] != 0) {
6041 p++;
6042 i++;
6043 int c;
6044 if (*p >= '0' && *p <= '9') {
6045 c = *p - '0';
6046 } else if (*p >= 'a' && *p <= 'f') {
6047 c = *p - 'a' + 10;
6048 } else if (*p >= 'A' && *p <= 'F') {
6049 c = *p - 'A' + 10;
6050 } else {
6051 if (outErrorMsg) {
6052 *outErrorMsg = "Bad character in \\u unicode escape sequence";
6053 }
6054 return false;
6055 }
6056 chr = (chr<<4) | c;
6057 }
6058 tmp.append(String16(&chr, 1));
6059 } break;
6060 default:
6061 // ignore unknown escape chars.
6062 break;
6063 }
6064 p++;
6065 }
6066 }
6067 len -= (p-s);
6068 s = p;
6069 }
6070 }
6071
6072 if (tmp.size() != 0) {
6073 if (len > 0) {
6074 tmp.append(String16(s, len));
6075 }
6076 if (append) {
6077 outString->append(tmp);
6078 } else {
6079 outString->setTo(tmp);
6080 }
6081 } else {
6082 if (append) {
6083 outString->append(String16(s, len));
6084 } else {
6085 outString->setTo(s, len);
6086 }
6087 }
6088
6089 return true;
6090 }
6091
getBasePackageCount() const6092 size_t ResTable::getBasePackageCount() const
6093 {
6094 if (mError != NO_ERROR) {
6095 return 0;
6096 }
6097 return mPackageGroups.size();
6098 }
6099
getBasePackageName(size_t idx) const6100 const String16 ResTable::getBasePackageName(size_t idx) const
6101 {
6102 if (mError != NO_ERROR) {
6103 return String16();
6104 }
6105 LOG_FATAL_IF(idx >= mPackageGroups.size(),
6106 "Requested package index %d past package count %d",
6107 (int)idx, (int)mPackageGroups.size());
6108 return mPackageGroups[idx]->name;
6109 }
6110
getBasePackageId(size_t idx) const6111 uint32_t ResTable::getBasePackageId(size_t idx) const
6112 {
6113 if (mError != NO_ERROR) {
6114 return 0;
6115 }
6116 LOG_FATAL_IF(idx >= mPackageGroups.size(),
6117 "Requested package index %d past package count %d",
6118 (int)idx, (int)mPackageGroups.size());
6119 return mPackageGroups[idx]->id;
6120 }
6121
getLastTypeIdForPackage(size_t idx) const6122 uint32_t ResTable::getLastTypeIdForPackage(size_t idx) const
6123 {
6124 if (mError != NO_ERROR) {
6125 return 0;
6126 }
6127 LOG_FATAL_IF(idx >= mPackageGroups.size(),
6128 "Requested package index %d past package count %d",
6129 (int)idx, (int)mPackageGroups.size());
6130 const PackageGroup* const group = mPackageGroups[idx];
6131 return group->largestTypeId;
6132 }
6133
getTableCount() const6134 size_t ResTable::getTableCount() const
6135 {
6136 return mHeaders.size();
6137 }
6138
getTableStringBlock(size_t index) const6139 const ResStringPool* ResTable::getTableStringBlock(size_t index) const
6140 {
6141 return &mHeaders[index]->values;
6142 }
6143
getTableCookie(size_t index) const6144 int32_t ResTable::getTableCookie(size_t index) const
6145 {
6146 return mHeaders[index]->cookie;
6147 }
6148
getDynamicRefTableForCookie(int32_t cookie) const6149 const DynamicRefTable* ResTable::getDynamicRefTableForCookie(int32_t cookie) const
6150 {
6151 const size_t N = mPackageGroups.size();
6152 for (size_t i = 0; i < N; i++) {
6153 const PackageGroup* pg = mPackageGroups[i];
6154 size_t M = pg->packages.size();
6155 for (size_t j = 0; j < M; j++) {
6156 if (pg->packages[j]->header->cookie == cookie) {
6157 return &pg->dynamicRefTable;
6158 }
6159 }
6160 }
6161 return NULL;
6162 }
6163
compareResTableConfig(const ResTable_config & a,const ResTable_config & b)6164 static bool compareResTableConfig(const ResTable_config& a, const ResTable_config& b) {
6165 return a.compare(b) < 0;
6166 }
6167
6168 template <typename Func>
forEachConfiguration(bool ignoreMipmap,bool ignoreAndroidPackage,bool includeSystemConfigs,const Func & f) const6169 void ResTable::forEachConfiguration(bool ignoreMipmap, bool ignoreAndroidPackage,
6170 bool includeSystemConfigs, const Func& f) const {
6171 const size_t packageCount = mPackageGroups.size();
6172 const String16 android("android");
6173 for (size_t i = 0; i < packageCount; i++) {
6174 const PackageGroup* packageGroup = mPackageGroups[i];
6175 if (ignoreAndroidPackage && android == packageGroup->name) {
6176 continue;
6177 }
6178 if (!includeSystemConfigs && packageGroup->isSystemAsset) {
6179 continue;
6180 }
6181 const size_t typeCount = packageGroup->types.size();
6182 for (size_t j = 0; j < typeCount; j++) {
6183 const TypeList& typeList = packageGroup->types[j];
6184 const size_t numTypes = typeList.size();
6185 for (size_t k = 0; k < numTypes; k++) {
6186 const Type* type = typeList[k];
6187 const ResStringPool& typeStrings = type->package->typeStrings;
6188 if (ignoreMipmap && typeStrings.string8ObjectAt(
6189 type->typeSpec->id - 1) == "mipmap") {
6190 continue;
6191 }
6192
6193 const size_t numConfigs = type->configs.size();
6194 for (size_t m = 0; m < numConfigs; m++) {
6195 const ResTable_type* config = type->configs[m];
6196 ResTable_config cfg;
6197 memset(&cfg, 0, sizeof(ResTable_config));
6198 cfg.copyFromDtoH(config->config);
6199
6200 f(cfg);
6201 }
6202 }
6203 }
6204 }
6205 }
6206
getConfigurations(Vector<ResTable_config> * configs,bool ignoreMipmap,bool ignoreAndroidPackage,bool includeSystemConfigs) const6207 void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap,
6208 bool ignoreAndroidPackage, bool includeSystemConfigs) const {
6209 auto func = [&](const ResTable_config& cfg) {
6210 const auto beginIter = configs->begin();
6211 const auto endIter = configs->end();
6212
6213 auto iter = std::lower_bound(beginIter, endIter, cfg, compareResTableConfig);
6214 if (iter == endIter || iter->compare(cfg) != 0) {
6215 configs->insertAt(cfg, std::distance(beginIter, iter));
6216 }
6217 };
6218 forEachConfiguration(ignoreMipmap, ignoreAndroidPackage, includeSystemConfigs, func);
6219 }
6220
compareString8AndCString(const String8 & str,const char * cStr)6221 static bool compareString8AndCString(const String8& str, const char* cStr) {
6222 return strcmp(str.string(), cStr) < 0;
6223 }
6224
getLocales(Vector<String8> * locales,bool includeSystemLocales,bool mergeEquivalentLangs) const6225 void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales,
6226 bool mergeEquivalentLangs) const {
6227 char locale[RESTABLE_MAX_LOCALE_LEN];
6228
6229 forEachConfiguration(false, false, includeSystemLocales, [&](const ResTable_config& cfg) {
6230 cfg.getBcp47Locale(locale, mergeEquivalentLangs /* canonicalize if merging */);
6231
6232 const auto beginIter = locales->begin();
6233 const auto endIter = locales->end();
6234
6235 auto iter = std::lower_bound(beginIter, endIter, locale, compareString8AndCString);
6236 if (iter == endIter || strcmp(iter->string(), locale) != 0) {
6237 locales->insertAt(String8(locale), std::distance(beginIter, iter));
6238 }
6239 });
6240 }
6241
StringPoolRef(const ResStringPool * pool,uint32_t index)6242 StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index)
6243 : mPool(pool), mIndex(index) {}
6244
string8(size_t * outLen) const6245 const char* StringPoolRef::string8(size_t* outLen) const {
6246 if (mPool != NULL) {
6247 return mPool->string8At(mIndex, outLen);
6248 }
6249 if (outLen != NULL) {
6250 *outLen = 0;
6251 }
6252 return NULL;
6253 }
6254
string16(size_t * outLen) const6255 const char16_t* StringPoolRef::string16(size_t* outLen) const {
6256 if (mPool != NULL) {
6257 return mPool->stringAt(mIndex, outLen);
6258 }
6259 if (outLen != NULL) {
6260 *outLen = 0;
6261 }
6262 return NULL;
6263 }
6264
getResourceFlags(uint32_t resID,uint32_t * outFlags) const6265 bool ResTable::getResourceFlags(uint32_t resID, uint32_t* outFlags) const {
6266 if (mError != NO_ERROR) {
6267 return false;
6268 }
6269
6270 const ssize_t p = getResourcePackageIndex(resID);
6271 const int t = Res_GETTYPE(resID);
6272 const int e = Res_GETENTRY(resID);
6273
6274 if (p < 0) {
6275 if (Res_GETPACKAGE(resID)+1 == 0) {
6276 ALOGW("No package identifier when getting flags for resource number 0x%08x", resID);
6277 } else {
6278 ALOGW("No known package when getting flags for resource number 0x%08x", resID);
6279 }
6280 return false;
6281 }
6282 if (t < 0) {
6283 ALOGW("No type identifier when getting flags for resource number 0x%08x", resID);
6284 return false;
6285 }
6286
6287 const PackageGroup* const grp = mPackageGroups[p];
6288 if (grp == NULL) {
6289 ALOGW("Bad identifier when getting flags for resource number 0x%08x", resID);
6290 return false;
6291 }
6292
6293 Entry entry;
6294 status_t err = getEntry(grp, t, e, NULL, &entry);
6295 if (err != NO_ERROR) {
6296 return false;
6297 }
6298
6299 *outFlags = entry.specFlags;
6300 return true;
6301 }
6302
isPackageDynamic(uint8_t packageID) const6303 bool ResTable::isPackageDynamic(uint8_t packageID) const {
6304 if (mError != NO_ERROR) {
6305 return false;
6306 }
6307 if (packageID == 0) {
6308 ALOGW("Invalid package number 0x%08x", packageID);
6309 return false;
6310 }
6311
6312 const ssize_t p = getResourcePackageIndexFromPackage(packageID);
6313
6314 if (p < 0) {
6315 ALOGW("Unknown package number 0x%08x", packageID);
6316 return false;
6317 }
6318
6319 const PackageGroup* const grp = mPackageGroups[p];
6320 if (grp == NULL) {
6321 ALOGW("Bad identifier for package number 0x%08x", packageID);
6322 return false;
6323 }
6324
6325 return grp->isDynamic;
6326 }
6327
isResourceDynamic(uint32_t resID) const6328 bool ResTable::isResourceDynamic(uint32_t resID) const {
6329 if (mError != NO_ERROR) {
6330 return false;
6331 }
6332
6333 const ssize_t p = getResourcePackageIndex(resID);
6334 const int t = Res_GETTYPE(resID);
6335 const int e = Res_GETENTRY(resID);
6336
6337 if (p < 0) {
6338 if (Res_GETPACKAGE(resID)+1 == 0) {
6339 ALOGW("No package identifier for resource number 0x%08x", resID);
6340 } else {
6341 ALOGW("No known package for resource number 0x%08x", resID);
6342 }
6343 return false;
6344 }
6345 if (t < 0) {
6346 ALOGW("No type identifier for resource number 0x%08x", resID);
6347 return false;
6348 }
6349
6350 const PackageGroup* const grp = mPackageGroups[p];
6351 if (grp == NULL) {
6352 ALOGW("Bad identifier for resource number 0x%08x", resID);
6353 return false;
6354 }
6355
6356 Entry entry;
6357 status_t err = getEntry(grp, t, e, NULL, &entry);
6358 if (err != NO_ERROR) {
6359 return false;
6360 }
6361
6362 return grp->isDynamic;
6363 }
6364
keyCompare(const ResTable_sparseTypeEntry & entry,uint16_t entryIdx)6365 static bool keyCompare(const ResTable_sparseTypeEntry& entry , uint16_t entryIdx) {
6366 return dtohs(entry.idx) < entryIdx;
6367 }
6368
getEntry(const PackageGroup * packageGroup,int typeIndex,int entryIndex,const ResTable_config * config,Entry * outEntry) const6369 status_t ResTable::getEntry(
6370 const PackageGroup* packageGroup, int typeIndex, int entryIndex,
6371 const ResTable_config* config,
6372 Entry* outEntry) const
6373 {
6374 const TypeList& typeList = packageGroup->types[typeIndex];
6375 if (typeList.isEmpty()) {
6376 ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
6377 return BAD_TYPE;
6378 }
6379
6380 const ResTable_type* bestType = NULL;
6381 uint32_t bestOffset = ResTable_type::NO_ENTRY;
6382 const Package* bestPackage = NULL;
6383 uint32_t specFlags = 0;
6384 uint8_t actualTypeIndex = typeIndex;
6385 ResTable_config bestConfig;
6386 memset(&bestConfig, 0, sizeof(bestConfig));
6387
6388 // Iterate over the Types of each package.
6389 const size_t typeCount = typeList.size();
6390 for (size_t i = 0; i < typeCount; i++) {
6391 const Type* const typeSpec = typeList[i];
6392
6393 int realEntryIndex = entryIndex;
6394 int realTypeIndex = typeIndex;
6395 bool currentTypeIsOverlay = false;
6396
6397 // Runtime overlay packages provide a mapping of app resource
6398 // ID to package resource ID.
6399 if (typeSpec->idmapEntries.hasEntries()) {
6400 uint16_t overlayEntryIndex;
6401 if (typeSpec->idmapEntries.lookup(entryIndex, &overlayEntryIndex) != NO_ERROR) {
6402 // No such mapping exists
6403 continue;
6404 }
6405 realEntryIndex = overlayEntryIndex;
6406 realTypeIndex = typeSpec->idmapEntries.overlayTypeId() - 1;
6407 currentTypeIsOverlay = true;
6408 }
6409
6410 // Check that the entry idx is within range of the declared entry count (ResTable_typeSpec).
6411 // Particular types (ResTable_type) may be encoded with sparse entries, and so their
6412 // entryCount do not need to match.
6413 if (static_cast<size_t>(realEntryIndex) >= typeSpec->entryCount) {
6414 ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)",
6415 Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex),
6416 entryIndex, static_cast<int>(typeSpec->entryCount));
6417 // We should normally abort here, but some legacy apps declare
6418 // resources in the 'android' package (old bug in AAPT).
6419 continue;
6420 }
6421
6422 // Aggregate all the flags for each package that defines this entry.
6423 if (typeSpec->typeSpecFlags != NULL) {
6424 specFlags |= dtohl(typeSpec->typeSpecFlags[realEntryIndex]);
6425 } else {
6426 specFlags = -1;
6427 }
6428
6429 const Vector<const ResTable_type*>* candidateConfigs = &typeSpec->configs;
6430
6431 std::shared_ptr<Vector<const ResTable_type*>> filteredConfigs;
6432 if (config && memcmp(&mParams, config, sizeof(mParams)) == 0) {
6433 // Grab the lock first so we can safely get the current filtered list.
6434 AutoMutex _lock(mFilteredConfigLock);
6435
6436 // This configuration is equal to the one we have previously cached for,
6437 // so use the filtered configs.
6438
6439 const TypeCacheEntry& cacheEntry = packageGroup->typeCacheEntries[typeIndex];
6440 if (i < cacheEntry.filteredConfigs.size()) {
6441 if (cacheEntry.filteredConfigs[i]) {
6442 // Grab a reference to the shared_ptr so it doesn't get destroyed while
6443 // going through this list.
6444 filteredConfigs = cacheEntry.filteredConfigs[i];
6445
6446 // Use this filtered list.
6447 candidateConfigs = filteredConfigs.get();
6448 }
6449 }
6450 }
6451
6452 const size_t numConfigs = candidateConfigs->size();
6453 for (size_t c = 0; c < numConfigs; c++) {
6454 const ResTable_type* const thisType = candidateConfigs->itemAt(c);
6455 if (thisType == NULL) {
6456 continue;
6457 }
6458
6459 ResTable_config thisConfig;
6460 thisConfig.copyFromDtoH(thisType->config);
6461
6462 // Check to make sure this one is valid for the current parameters.
6463 if (config != NULL && !thisConfig.match(*config)) {
6464 continue;
6465 }
6466
6467 const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
6468 reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));
6469
6470 uint32_t thisOffset;
6471
6472 // Check if there is the desired entry in this type.
6473 if (thisType->flags & ResTable_type::FLAG_SPARSE) {
6474 // This is encoded as a sparse map, so perform a binary search.
6475 const ResTable_sparseTypeEntry* sparseIndices =
6476 reinterpret_cast<const ResTable_sparseTypeEntry*>(eindex);
6477 const ResTable_sparseTypeEntry* result = std::lower_bound(
6478 sparseIndices, sparseIndices + dtohl(thisType->entryCount), realEntryIndex,
6479 keyCompare);
6480 if (result == sparseIndices + dtohl(thisType->entryCount)
6481 || dtohs(result->idx) != realEntryIndex) {
6482 // No entry found.
6483 continue;
6484 }
6485
6486 // Extract the offset from the entry. Each offset must be a multiple of 4
6487 // so we store it as the real offset divided by 4.
6488 thisOffset = dtohs(result->offset) * 4u;
6489 } else {
6490 if (static_cast<uint32_t>(realEntryIndex) >= dtohl(thisType->entryCount)) {
6491 // Entry does not exist.
6492 continue;
6493 }
6494
6495 thisOffset = dtohl(eindex[realEntryIndex]);
6496 }
6497
6498 if (thisOffset == ResTable_type::NO_ENTRY) {
6499 // There is no entry for this index and configuration.
6500 continue;
6501 }
6502
6503 if (bestType != NULL) {
6504 // Check if this one is less specific than the last found. If so,
6505 // we will skip it. We check starting with things we most care
6506 // about to those we least care about.
6507 if (!thisConfig.isBetterThan(bestConfig, config)) {
6508 if (!currentTypeIsOverlay || thisConfig.compare(bestConfig) != 0) {
6509 continue;
6510 }
6511 }
6512 }
6513
6514 bestType = thisType;
6515 bestOffset = thisOffset;
6516 bestConfig = thisConfig;
6517 bestPackage = typeSpec->package;
6518 actualTypeIndex = realTypeIndex;
6519
6520 // If no config was specified, any type will do, so skip
6521 if (config == NULL) {
6522 break;
6523 }
6524 }
6525 }
6526
6527 if (bestType == NULL) {
6528 return BAD_INDEX;
6529 }
6530
6531 bestOffset += dtohl(bestType->entriesStart);
6532
6533 if (bestOffset > (dtohl(bestType->header.size)-sizeof(ResTable_entry))) {
6534 ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x",
6535 bestOffset, dtohl(bestType->header.size));
6536 return BAD_TYPE;
6537 }
6538 if ((bestOffset & 0x3) != 0) {
6539 ALOGW("ResTable_entry at 0x%x is not on an integer boundary", bestOffset);
6540 return BAD_TYPE;
6541 }
6542
6543 const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>(
6544 reinterpret_cast<const uint8_t*>(bestType) + bestOffset);
6545 if (dtohs(entry->size) < sizeof(*entry)) {
6546 ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size));
6547 return BAD_TYPE;
6548 }
6549
6550 if (outEntry != NULL) {
6551 outEntry->entry = entry;
6552 outEntry->config = bestConfig;
6553 outEntry->type = bestType;
6554 outEntry->specFlags = specFlags;
6555 outEntry->package = bestPackage;
6556 outEntry->typeStr = StringPoolRef(&bestPackage->typeStrings, actualTypeIndex - bestPackage->typeIdOffset);
6557 outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, dtohl(entry->key.index));
6558 }
6559 return NO_ERROR;
6560 }
6561
parsePackage(const ResTable_package * const pkg,const Header * const header,bool appAsLib,bool isSystemAsset)6562 status_t ResTable::parsePackage(const ResTable_package* const pkg,
6563 const Header* const header, bool appAsLib, bool isSystemAsset)
6564 {
6565 const uint8_t* base = (const uint8_t*)pkg;
6566 status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset),
6567 header->dataEnd, "ResTable_package");
6568 if (err != NO_ERROR) {
6569 return (mError=err);
6570 }
6571
6572 const uint32_t pkgSize = dtohl(pkg->header.size);
6573
6574 if (dtohl(pkg->typeStrings) >= pkgSize) {
6575 ALOGW("ResTable_package type strings at 0x%x are past chunk size 0x%x.",
6576 dtohl(pkg->typeStrings), pkgSize);
6577 return (mError=BAD_TYPE);
6578 }
6579 if ((dtohl(pkg->typeStrings)&0x3) != 0) {
6580 ALOGW("ResTable_package type strings at 0x%x is not on an integer boundary.",
6581 dtohl(pkg->typeStrings));
6582 return (mError=BAD_TYPE);
6583 }
6584 if (dtohl(pkg->keyStrings) >= pkgSize) {
6585 ALOGW("ResTable_package key strings at 0x%x are past chunk size 0x%x.",
6586 dtohl(pkg->keyStrings), pkgSize);
6587 return (mError=BAD_TYPE);
6588 }
6589 if ((dtohl(pkg->keyStrings)&0x3) != 0) {
6590 ALOGW("ResTable_package key strings at 0x%x is not on an integer boundary.",
6591 dtohl(pkg->keyStrings));
6592 return (mError=BAD_TYPE);
6593 }
6594
6595 uint32_t id = dtohl(pkg->id);
6596 KeyedVector<uint8_t, IdmapEntries> idmapEntries;
6597
6598 if (header->resourceIDMap != NULL) {
6599 uint8_t targetPackageId = 0;
6600 status_t err = parseIdmap(header->resourceIDMap, header->resourceIDMapSize, &targetPackageId, &idmapEntries);
6601 if (err != NO_ERROR) {
6602 ALOGW("Overlay is broken");
6603 return (mError=err);
6604 }
6605 id = targetPackageId;
6606 }
6607
6608 bool isDynamic = false;
6609 if (id >= 256) {
6610 LOG_ALWAYS_FATAL("Package id out of range");
6611 return NO_ERROR;
6612 } else if (id == 0 || (id == 0x7f && appAsLib) || isSystemAsset) {
6613 // This is a library or a system asset, so assign an ID
6614 id = mNextPackageId++;
6615 isDynamic = true;
6616 }
6617
6618 PackageGroup* group = NULL;
6619 Package* package = new Package(this, header, pkg);
6620 if (package == NULL) {
6621 return (mError=NO_MEMORY);
6622 }
6623
6624 err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
6625 header->dataEnd-(base+dtohl(pkg->typeStrings)));
6626 if (err != NO_ERROR) {
6627 delete group;
6628 delete package;
6629 return (mError=err);
6630 }
6631
6632 err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
6633 header->dataEnd-(base+dtohl(pkg->keyStrings)));
6634 if (err != NO_ERROR) {
6635 delete group;
6636 delete package;
6637 return (mError=err);
6638 }
6639
6640 size_t idx = mPackageMap[id];
6641 if (idx == 0) {
6642 idx = mPackageGroups.size() + 1;
6643 char16_t tmpName[sizeof(pkg->name)/sizeof(pkg->name[0])];
6644 strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(pkg->name[0]));
6645 group = new PackageGroup(this, String16(tmpName), id, appAsLib, isSystemAsset, isDynamic);
6646 if (group == NULL) {
6647 delete package;
6648 return (mError=NO_MEMORY);
6649 }
6650
6651 err = mPackageGroups.add(group);
6652 if (err < NO_ERROR) {
6653 return (mError=err);
6654 }
6655
6656 mPackageMap[id] = static_cast<uint8_t>(idx);
6657
6658 // Find all packages that reference this package
6659 size_t N = mPackageGroups.size();
6660 for (size_t i = 0; i < N; i++) {
6661 mPackageGroups[i]->dynamicRefTable.addMapping(
6662 group->name, static_cast<uint8_t>(group->id));
6663 }
6664 } else {
6665 group = mPackageGroups.itemAt(idx - 1);
6666 if (group == NULL) {
6667 return (mError=UNKNOWN_ERROR);
6668 }
6669 }
6670
6671 err = group->packages.add(package);
6672 if (err < NO_ERROR) {
6673 return (mError=err);
6674 }
6675
6676 // Iterate through all chunks.
6677 const ResChunk_header* chunk =
6678 (const ResChunk_header*)(((const uint8_t*)pkg)
6679 + dtohs(pkg->header.headerSize));
6680 const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size);
6681 while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) &&
6682 ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) {
6683 if (kDebugTableNoisy) {
6684 ALOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n",
6685 dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size),
6686 (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)));
6687 }
6688 const size_t csize = dtohl(chunk->size);
6689 const uint16_t ctype = dtohs(chunk->type);
6690 if (ctype == RES_TABLE_TYPE_SPEC_TYPE) {
6691 const ResTable_typeSpec* typeSpec = (const ResTable_typeSpec*)(chunk);
6692 err = validate_chunk(&typeSpec->header, sizeof(*typeSpec),
6693 endPos, "ResTable_typeSpec");
6694 if (err != NO_ERROR) {
6695 return (mError=err);
6696 }
6697
6698 const size_t typeSpecSize = dtohl(typeSpec->header.size);
6699 const size_t newEntryCount = dtohl(typeSpec->entryCount);
6700
6701 if (kDebugLoadTableNoisy) {
6702 ALOGI("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n",
6703 (void*)(base-(const uint8_t*)chunk),
6704 dtohs(typeSpec->header.type),
6705 dtohs(typeSpec->header.headerSize),
6706 (void*)typeSpecSize);
6707 }
6708 // look for block overrun or int overflow when multiplying by 4
6709 if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t))
6710 || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*newEntryCount)
6711 > typeSpecSize)) {
6712 ALOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.",
6713 (void*)(dtohs(typeSpec->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
6714 (void*)typeSpecSize);
6715 return (mError=BAD_TYPE);
6716 }
6717
6718 if (typeSpec->id == 0) {
6719 ALOGW("ResTable_type has an id of 0.");
6720 return (mError=BAD_TYPE);
6721 }
6722
6723 if (newEntryCount > 0) {
6724 bool addToType = true;
6725 uint8_t typeIndex = typeSpec->id - 1;
6726 ssize_t idmapIndex = idmapEntries.indexOfKey(typeSpec->id);
6727 if (idmapIndex >= 0) {
6728 typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
6729 } else if (header->resourceIDMap != NULL) {
6730 // This is an overlay, but the types in this overlay are not
6731 // overlaying anything according to the idmap. We can skip these
6732 // as they will otherwise conflict with the other resources in the package
6733 // without a mapping.
6734 addToType = false;
6735 }
6736
6737 if (addToType) {
6738 TypeList& typeList = group->types.editItemAt(typeIndex);
6739 if (!typeList.isEmpty()) {
6740 const Type* existingType = typeList[0];
6741 if (existingType->entryCount != newEntryCount && idmapIndex < 0) {
6742 ALOGW("ResTable_typeSpec entry count inconsistent: "
6743 "given %d, previously %d",
6744 (int) newEntryCount, (int) existingType->entryCount);
6745 // We should normally abort here, but some legacy apps declare
6746 // resources in the 'android' package (old bug in AAPT).
6747 }
6748 }
6749
6750 Type* t = new Type(header, package, newEntryCount);
6751 t->typeSpec = typeSpec;
6752 t->typeSpecFlags = (const uint32_t*)(
6753 ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
6754 if (idmapIndex >= 0) {
6755 t->idmapEntries = idmapEntries[idmapIndex];
6756 }
6757 typeList.add(t);
6758 group->largestTypeId = max(group->largestTypeId, typeSpec->id);
6759 }
6760 } else {
6761 ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec->id);
6762 }
6763
6764 } else if (ctype == RES_TABLE_TYPE_TYPE) {
6765 const ResTable_type* type = (const ResTable_type*)(chunk);
6766 err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4,
6767 endPos, "ResTable_type");
6768 if (err != NO_ERROR) {
6769 return (mError=err);
6770 }
6771
6772 const uint32_t typeSize = dtohl(type->header.size);
6773 const size_t newEntryCount = dtohl(type->entryCount);
6774
6775 if (kDebugLoadTableNoisy) {
6776 printf("Type off %p: type=0x%x, headerSize=0x%x, size=%u\n",
6777 (void*)(base-(const uint8_t*)chunk),
6778 dtohs(type->header.type),
6779 dtohs(type->header.headerSize),
6780 typeSize);
6781 }
6782 if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount) > typeSize) {
6783 ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.",
6784 (void*)(dtohs(type->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
6785 typeSize);
6786 return (mError=BAD_TYPE);
6787 }
6788
6789 if (newEntryCount != 0
6790 && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) {
6791 ALOGW("ResTable_type entriesStart at 0x%x extends beyond chunk end 0x%x.",
6792 dtohl(type->entriesStart), typeSize);
6793 return (mError=BAD_TYPE);
6794 }
6795
6796 if (type->id == 0) {
6797 ALOGW("ResTable_type has an id of 0.");
6798 return (mError=BAD_TYPE);
6799 }
6800
6801 if (newEntryCount > 0) {
6802 bool addToType = true;
6803 uint8_t typeIndex = type->id - 1;
6804 ssize_t idmapIndex = idmapEntries.indexOfKey(type->id);
6805 if (idmapIndex >= 0) {
6806 typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
6807 } else if (header->resourceIDMap != NULL) {
6808 // This is an overlay, but the types in this overlay are not
6809 // overlaying anything according to the idmap. We can skip these
6810 // as they will otherwise conflict with the other resources in the package
6811 // without a mapping.
6812 addToType = false;
6813 }
6814
6815 if (addToType) {
6816 TypeList& typeList = group->types.editItemAt(typeIndex);
6817 if (typeList.isEmpty()) {
6818 ALOGE("No TypeSpec for type %d", type->id);
6819 return (mError=BAD_TYPE);
6820 }
6821
6822 Type* t = typeList.editItemAt(typeList.size() - 1);
6823 if (t->package != package) {
6824 ALOGE("No TypeSpec for type %d", type->id);
6825 return (mError=BAD_TYPE);
6826 }
6827
6828 t->configs.add(type);
6829
6830 if (kDebugTableGetEntry) {
6831 ResTable_config thisConfig;
6832 thisConfig.copyFromDtoH(type->config);
6833 ALOGI("Adding config to type %d: %s\n", type->id,
6834 thisConfig.toString().string());
6835 }
6836 }
6837 } else {
6838 ALOGV("Skipping empty ResTable_type for type %d", type->id);
6839 }
6840
6841 } else if (ctype == RES_TABLE_LIBRARY_TYPE) {
6842
6843 if (group->dynamicRefTable.entries().size() == 0) {
6844 const ResTable_lib_header* lib = (const ResTable_lib_header*) chunk;
6845 status_t err = validate_chunk(&lib->header, sizeof(*lib),
6846 endPos, "ResTable_lib_header");
6847 if (err != NO_ERROR) {
6848 return (mError=err);
6849 }
6850
6851 err = group->dynamicRefTable.load(lib);
6852 if (err != NO_ERROR) {
6853 return (mError=err);
6854 }
6855
6856 // Fill in the reference table with the entries we already know about.
6857 size_t N = mPackageGroups.size();
6858 for (size_t i = 0; i < N; i++) {
6859 group->dynamicRefTable.addMapping(mPackageGroups[i]->name, mPackageGroups[i]->id);
6860 }
6861 } else {
6862 ALOGW("Found multiple library tables, ignoring...");
6863 }
6864 } else {
6865 if (ctype == RES_TABLE_OVERLAYABLE_TYPE) {
6866 package->definesOverlayable = true;
6867 }
6868
6869 status_t err = validate_chunk(chunk, sizeof(ResChunk_header),
6870 endPos, "ResTable_package:unknown");
6871 if (err != NO_ERROR) {
6872 return (mError=err);
6873 }
6874 }
6875 chunk = (const ResChunk_header*)
6876 (((const uint8_t*)chunk) + csize);
6877 }
6878
6879 return NO_ERROR;
6880 }
6881
DynamicRefTable()6882 DynamicRefTable::DynamicRefTable() : DynamicRefTable(0, false) {}
6883
DynamicRefTable(uint8_t packageId,bool appAsLib)6884 DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
6885 : mAssignedPackageId(packageId)
6886 , mAppAsLib(appAsLib)
6887 {
6888 memset(mLookupTable, 0, sizeof(mLookupTable));
6889
6890 // Reserved package ids
6891 mLookupTable[APP_PACKAGE_ID] = APP_PACKAGE_ID;
6892 mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID;
6893 }
6894
clone() const6895 std::unique_ptr<DynamicRefTable> DynamicRefTable::clone() const {
6896 std::unique_ptr<DynamicRefTable> clone = std::unique_ptr<DynamicRefTable>(
6897 new DynamicRefTable(mAssignedPackageId, mAppAsLib));
6898 clone->addMappings(*this);
6899 return clone;
6900 }
6901
load(const ResTable_lib_header * const header)6902 status_t DynamicRefTable::load(const ResTable_lib_header* const header)
6903 {
6904 const uint32_t entryCount = dtohl(header->count);
6905 const uint32_t expectedSize = dtohl(header->header.size) - dtohl(header->header.headerSize);
6906 if (entryCount > (expectedSize / sizeof(ResTable_lib_entry))) {
6907 ALOGE("ResTable_lib_header size %u is too small to fit %u entries (x %u).",
6908 expectedSize, entryCount, (uint32_t)sizeof(ResTable_lib_entry));
6909 return UNKNOWN_ERROR;
6910 }
6911
6912 const ResTable_lib_entry* entry = (const ResTable_lib_entry*)(((uint8_t*) header) +
6913 dtohl(header->header.headerSize));
6914 for (uint32_t entryIndex = 0; entryIndex < entryCount; entryIndex++) {
6915 uint32_t packageId = dtohl(entry->packageId);
6916 char16_t tmpName[sizeof(entry->packageName) / sizeof(char16_t)];
6917 strcpy16_dtoh(tmpName, entry->packageName, sizeof(entry->packageName) / sizeof(char16_t));
6918 if (kDebugLibNoisy) {
6919 ALOGV("Found lib entry %s with id %d\n", String8(tmpName).string(),
6920 dtohl(entry->packageId));
6921 }
6922 if (packageId >= 256) {
6923 ALOGE("Bad package id 0x%08x", packageId);
6924 return UNKNOWN_ERROR;
6925 }
6926 mEntries.replaceValueFor(String16(tmpName), (uint8_t) packageId);
6927 entry = entry + 1;
6928 }
6929 return NO_ERROR;
6930 }
6931
addMappings(const DynamicRefTable & other)6932 status_t DynamicRefTable::addMappings(const DynamicRefTable& other) {
6933 if (mAssignedPackageId != other.mAssignedPackageId) {
6934 return UNKNOWN_ERROR;
6935 }
6936
6937 const size_t entryCount = other.mEntries.size();
6938 for (size_t i = 0; i < entryCount; i++) {
6939 ssize_t index = mEntries.indexOfKey(other.mEntries.keyAt(i));
6940 if (index < 0) {
6941 mEntries.add(String16(other.mEntries.keyAt(i)), other.mEntries[i]);
6942 } else {
6943 if (other.mEntries[i] != mEntries[index]) {
6944 return UNKNOWN_ERROR;
6945 }
6946 }
6947 }
6948
6949 // Merge the lookup table. No entry can conflict
6950 // (value of 0 means not set).
6951 for (size_t i = 0; i < 256; i++) {
6952 if (mLookupTable[i] != other.mLookupTable[i]) {
6953 if (mLookupTable[i] == 0) {
6954 mLookupTable[i] = other.mLookupTable[i];
6955 } else if (other.mLookupTable[i] != 0) {
6956 return UNKNOWN_ERROR;
6957 }
6958 }
6959 }
6960 return NO_ERROR;
6961 }
6962
addMapping(const String16 & packageName,uint8_t packageId)6963 status_t DynamicRefTable::addMapping(const String16& packageName, uint8_t packageId)
6964 {
6965 ssize_t index = mEntries.indexOfKey(packageName);
6966 if (index < 0) {
6967 return UNKNOWN_ERROR;
6968 }
6969 mLookupTable[mEntries.valueAt(index)] = packageId;
6970 return NO_ERROR;
6971 }
6972
addMapping(uint8_t buildPackageId,uint8_t runtimePackageId)6973 void DynamicRefTable::addMapping(uint8_t buildPackageId, uint8_t runtimePackageId) {
6974 mLookupTable[buildPackageId] = runtimePackageId;
6975 }
6976
lookupResourceId(uint32_t * resId) const6977 status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
6978 uint32_t res = *resId;
6979 size_t packageId = Res_GETPACKAGE(res) + 1;
6980
6981 if (!Res_VALIDID(res)) {
6982 // Cannot look up a null or invalid id, so no lookup needs to be done.
6983 return NO_ERROR;
6984 }
6985
6986 if (packageId == APP_PACKAGE_ID && !mAppAsLib) {
6987 // No lookup needs to be done, app package IDs are absolute.
6988 return NO_ERROR;
6989 }
6990
6991 if (packageId == 0 || (packageId == APP_PACKAGE_ID && mAppAsLib)) {
6992 // The package ID is 0x00. That means that a shared library is accessing
6993 // its own local resource.
6994 // Or if app resource is loaded as shared library, the resource which has
6995 // app package Id is local resources.
6996 // so we fix up those resources with the calling package ID.
6997 *resId = (0xFFFFFF & (*resId)) | (((uint32_t) mAssignedPackageId) << 24);
6998 return NO_ERROR;
6999 }
7000
7001 // Do a proper lookup.
7002 uint8_t translatedId = mLookupTable[packageId];
7003 if (translatedId == 0) {
7004 ALOGW("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.",
7005 (uint8_t)mAssignedPackageId, (uint8_t)packageId);
7006 for (size_t i = 0; i < 256; i++) {
7007 if (mLookupTable[i] != 0) {
7008 ALOGW("e[0x%02x] -> 0x%02x", (uint8_t)i, mLookupTable[i]);
7009 }
7010 }
7011 return UNKNOWN_ERROR;
7012 }
7013
7014 *resId = (res & 0x00ffffff) | (((uint32_t) translatedId) << 24);
7015 return NO_ERROR;
7016 }
7017
lookupResourceValue(Res_value * value) const7018 status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
7019 uint8_t resolvedType = Res_value::TYPE_REFERENCE;
7020 switch (value->dataType) {
7021 case Res_value::TYPE_ATTRIBUTE:
7022 resolvedType = Res_value::TYPE_ATTRIBUTE;
7023 FALLTHROUGH_INTENDED;
7024 case Res_value::TYPE_REFERENCE:
7025 // Only resolve non-dynamic references and attributes if the package is loaded as a
7026 // library or if a shared library is attempting to retrieve its own resource
7027 if (!(mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) {
7028 return NO_ERROR;
7029 }
7030
7031 // If the package is loaded as shared library, the resource reference
7032 // also need to be fixed.
7033 break;
7034 case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
7035 resolvedType = Res_value::TYPE_ATTRIBUTE;
7036 FALLTHROUGH_INTENDED;
7037 case Res_value::TYPE_DYNAMIC_REFERENCE:
7038 break;
7039 default:
7040 return NO_ERROR;
7041 }
7042
7043 status_t err = lookupResourceId(&value->data);
7044 if (err != NO_ERROR) {
7045 return err;
7046 }
7047
7048 value->dataType = resolvedType;
7049 return NO_ERROR;
7050 }
7051
7052 class IdmapMatchingResources;
7053
7054 class IdmapTypeMapping {
7055 public:
add(uint32_t targetResId,uint32_t overlayResId)7056 void add(uint32_t targetResId, uint32_t overlayResId) {
7057 uint8_t targetTypeId = Res_GETTYPE(targetResId);
7058 if (mData.find(targetTypeId) == mData.end()) {
7059 mData.emplace(targetTypeId, std::set<std::pair<uint32_t, uint32_t>>());
7060 }
7061 auto& entries = mData[targetTypeId];
7062 entries.insert(std::make_pair(targetResId, overlayResId));
7063 }
7064
empty() const7065 bool empty() const {
7066 return mData.empty();
7067 }
7068
7069 private:
7070 // resource type ID in context of target -> set of resource entries mapping target -> overlay
7071 std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>> mData;
7072
7073 friend IdmapMatchingResources;
7074 };
7075
7076 class IdmapMatchingResources {
7077 public:
IdmapMatchingResources(std::unique_ptr<IdmapTypeMapping> tm)7078 IdmapMatchingResources(std::unique_ptr<IdmapTypeMapping> tm) : mTypeMapping(std::move(tm)) {
7079 assert(mTypeMapping);
7080 for (auto ti = mTypeMapping->mData.cbegin(); ti != mTypeMapping->mData.cend(); ++ti) {
7081 uint32_t lastSeen = 0xffffffff;
7082 size_t totalEntries = 0;
7083 for (auto ei = ti->second.cbegin(); ei != ti->second.cend(); ++ei) {
7084 assert(lastSeen == 0xffffffff || lastSeen < ei->first);
7085 mEntryPadding[ei->first] = (lastSeen == 0xffffffff) ? 0 : ei->first - lastSeen - 1;
7086 lastSeen = ei->first;
7087 totalEntries += 1 + mEntryPadding[ei->first];
7088 }
7089 mNumberOfEntriesIncludingPadding[ti->first] = totalEntries;
7090 }
7091 }
7092
getTypeMapping() const7093 const std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>>& getTypeMapping() const {
7094 return mTypeMapping->mData;
7095 }
7096
getNumberOfEntriesIncludingPadding(uint8_t type) const7097 size_t getNumberOfEntriesIncludingPadding(uint8_t type) const {
7098 return mNumberOfEntriesIncludingPadding.at(type);
7099 }
7100
getPadding(uint32_t resid) const7101 size_t getPadding(uint32_t resid) const {
7102 return mEntryPadding.at(resid);
7103 }
7104
7105 private:
7106 // resource type ID in context of target -> set of resource entries mapping target -> overlay
7107 const std::unique_ptr<IdmapTypeMapping> mTypeMapping;
7108
7109 // resource ID in context of target -> trailing padding for that resource (call FixPadding
7110 // before use)
7111 std::map<uint32_t, size_t> mEntryPadding;
7112
7113 // resource type ID in context of target -> total number of entries, including padding entries,
7114 // for that type (call FixPadding before use)
7115 std::map<uint8_t, size_t> mNumberOfEntriesIncludingPadding;
7116 };
7117
createIdmap(const ResTable & targetResTable,uint32_t targetCrc,uint32_t overlayCrc,const char * targetPath,const char * overlayPath,void ** outData,size_t * outSize) const7118 status_t ResTable::createIdmap(const ResTable& targetResTable,
7119 uint32_t targetCrc, uint32_t overlayCrc,
7120 const char* targetPath, const char* overlayPath,
7121 void** outData, size_t* outSize) const
7122 {
7123 if (targetPath == NULL || overlayPath == NULL || outData == NULL || outSize == NULL) {
7124 ALOGE("idmap: unexpected NULL parameter");
7125 return UNKNOWN_ERROR;
7126 }
7127 if (strlen(targetPath) > 255) {
7128 ALOGE("idmap: target path exceeds idmap file format limit of 255 chars");
7129 return UNKNOWN_ERROR;
7130 }
7131 if (strlen(overlayPath) > 255) {
7132 ALOGE("idmap: overlay path exceeds idmap file format limit of 255 chars");
7133 return UNKNOWN_ERROR;
7134 }
7135 if (mPackageGroups.size() == 0 || mPackageGroups[0]->packages.size() == 0) {
7136 ALOGE("idmap: invalid overlay package");
7137 return UNKNOWN_ERROR;
7138 }
7139 if (targetResTable.mPackageGroups.size() == 0 ||
7140 targetResTable.mPackageGroups[0]->packages.size() == 0) {
7141 ALOGE("idmap: invalid target package");
7142 return UNKNOWN_ERROR;
7143 }
7144
7145 // Idmap is not aware of overlayable, exit since policy checks can't be done
7146 if (targetResTable.mPackageGroups[0]->packages[0]->definesOverlayable) {
7147 return UNKNOWN_ERROR;
7148 }
7149
7150 const ResTable_package* targetPackageStruct =
7151 targetResTable.mPackageGroups[0]->packages[0]->package;
7152 const size_t tmpNameSize = arraysize(targetPackageStruct->name);
7153 char16_t tmpName[tmpNameSize];
7154 strcpy16_dtoh(tmpName, targetPackageStruct->name, tmpNameSize);
7155 const String16 targetPackageName(tmpName);
7156
7157 const PackageGroup* packageGroup = mPackageGroups[0];
7158
7159 // find the resources that exist in both packages
7160 auto typeMapping = std::make_unique<IdmapTypeMapping>();
7161 for (size_t typeIndex = 0; typeIndex < packageGroup->types.size(); ++typeIndex) {
7162 const TypeList& typeList = packageGroup->types[typeIndex];
7163 if (typeList.isEmpty()) {
7164 continue;
7165 }
7166 const Type* typeConfigs = typeList[0];
7167
7168 for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
7169 uint32_t overlay_resid = Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex);
7170 resource_name current_res;
7171 if (!getResourceName(overlay_resid, false, ¤t_res)) {
7172 continue;
7173 }
7174
7175 uint32_t typeSpecFlags = 0u;
7176 const uint32_t target_resid = targetResTable.identifierForName(
7177 current_res.name,
7178 current_res.nameLen,
7179 current_res.type,
7180 current_res.typeLen,
7181 targetPackageName.string(),
7182 targetPackageName.size(),
7183 &typeSpecFlags);
7184
7185 if (target_resid == 0) {
7186 continue;
7187 }
7188
7189 typeMapping->add(target_resid, overlay_resid);
7190 }
7191 }
7192
7193 if (typeMapping->empty()) {
7194 ALOGE("idmap: no matching resources");
7195 return UNKNOWN_ERROR;
7196 }
7197
7198 const IdmapMatchingResources matchingResources(std::move(typeMapping));
7199
7200 // write idmap
7201 *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES; // magic, version, target and overlay crc
7202 *outSize += 2 * sizeof(uint16_t); // target package id, type count
7203 auto fixedTypeMapping = matchingResources.getTypeMapping();
7204 const auto typesEnd = fixedTypeMapping.cend();
7205 for (auto ti = fixedTypeMapping.cbegin(); ti != typesEnd; ++ti) {
7206 *outSize += 4 * sizeof(uint16_t); // target type, overlay type, entry count, entry offset
7207 *outSize += matchingResources.getNumberOfEntriesIncludingPadding(ti->first) *
7208 sizeof(uint32_t); // entries
7209 }
7210 if ((*outData = malloc(*outSize)) == NULL) {
7211 return NO_MEMORY;
7212 }
7213
7214 // write idmap header
7215 uint32_t* data = reinterpret_cast<uint32_t*>(*outData);
7216 *data++ = htodl(IDMAP_MAGIC); // write: magic
7217 *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION); // write: version
7218 *data++ = htodl(targetCrc); // write: target crc
7219 *data++ = htodl(overlayCrc); // write: overlay crc
7220
7221 char* charData = reinterpret_cast<char*>(data);
7222 size_t pathLen = strlen(targetPath);
7223 for (size_t i = 0; i < 256; ++i) {
7224 *charData++ = i < pathLen ? targetPath[i] : '\0'; // write: target path
7225 }
7226 pathLen = strlen(overlayPath);
7227 for (size_t i = 0; i < 256; ++i) {
7228 *charData++ = i < pathLen ? overlayPath[i] : '\0'; // write: overlay path
7229 }
7230 data += (2 * 256) / sizeof(uint32_t);
7231
7232 // write idmap data header
7233 uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
7234 *typeData++ = htods(targetPackageStruct->id); // write: target package id
7235 *typeData++ =
7236 htods(static_cast<uint16_t>(fixedTypeMapping.size())); // write: type count
7237
7238 // write idmap data
7239 for (auto ti = fixedTypeMapping.cbegin(); ti != typesEnd; ++ti) {
7240 const size_t entryCount = matchingResources.getNumberOfEntriesIncludingPadding(ti->first);
7241 auto ei = ti->second.cbegin();
7242 *typeData++ = htods(Res_GETTYPE(ei->first) + 1); // write: target type id
7243 *typeData++ = htods(Res_GETTYPE(ei->second) + 1); // write: overlay type id
7244 *typeData++ = htods(entryCount); // write: entry count
7245 *typeData++ = htods(Res_GETENTRY(ei->first)); // write: (target) entry offset
7246 uint32_t *entryData = reinterpret_cast<uint32_t*>(typeData);
7247 for (; ei != ti->second.cend(); ++ei) {
7248 const size_t padding = matchingResources.getPadding(ei->first);
7249 for (size_t i = 0; i < padding; ++i) {
7250 *entryData++ = htodl(0xffffffff); // write: padding
7251 }
7252 *entryData++ = htodl(Res_GETENTRY(ei->second)); // write: (overlay) entry
7253 }
7254 typeData += entryCount * 2;
7255 }
7256
7257 return NO_ERROR;
7258 }
7259
getIdmapInfo(const void * idmap,size_t sizeBytes,uint32_t * pVersion,uint32_t * pTargetCrc,uint32_t * pOverlayCrc,String8 * pTargetPath,String8 * pOverlayPath)7260 bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
7261 uint32_t* pVersion,
7262 uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
7263 String8* pTargetPath, String8* pOverlayPath)
7264 {
7265 const uint32_t* map = (const uint32_t*)idmap;
7266 if (!assertIdmapHeader(map, sizeBytes)) {
7267 return false;
7268 }
7269 if (pVersion) {
7270 *pVersion = dtohl(map[1]);
7271 }
7272 if (pTargetCrc) {
7273 *pTargetCrc = dtohl(map[2]);
7274 }
7275 if (pOverlayCrc) {
7276 *pOverlayCrc = dtohl(map[3]);
7277 }
7278 if (pTargetPath) {
7279 pTargetPath->setTo(reinterpret_cast<const char*>(map + 4));
7280 }
7281 if (pOverlayPath) {
7282 pOverlayPath->setTo(reinterpret_cast<const char*>(map + 4 + 256 / sizeof(uint32_t)));
7283 }
7284 return true;
7285 }
7286
7287
7288 #define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string())
7289
7290 #define CHAR16_ARRAY_EQ(constant, var, len) \
7291 (((len) == (sizeof(constant)/sizeof((constant)[0]))) && (0 == memcmp((var), (constant), (len))))
7292
print_complex(uint32_t complex,bool isFraction)7293 static void print_complex(uint32_t complex, bool isFraction)
7294 {
7295 const float MANTISSA_MULT =
7296 1.0f / (1<<Res_value::COMPLEX_MANTISSA_SHIFT);
7297 const float RADIX_MULTS[] = {
7298 1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT,
7299 1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT
7300 };
7301
7302 float value = (complex&(Res_value::COMPLEX_MANTISSA_MASK
7303 <<Res_value::COMPLEX_MANTISSA_SHIFT))
7304 * RADIX_MULTS[(complex>>Res_value::COMPLEX_RADIX_SHIFT)
7305 & Res_value::COMPLEX_RADIX_MASK];
7306 printf("%f", value);
7307
7308 if (!isFraction) {
7309 switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
7310 case Res_value::COMPLEX_UNIT_PX: printf("px"); break;
7311 case Res_value::COMPLEX_UNIT_DIP: printf("dp"); break;
7312 case Res_value::COMPLEX_UNIT_SP: printf("sp"); break;
7313 case Res_value::COMPLEX_UNIT_PT: printf("pt"); break;
7314 case Res_value::COMPLEX_UNIT_IN: printf("in"); break;
7315 case Res_value::COMPLEX_UNIT_MM: printf("mm"); break;
7316 default: printf(" (unknown unit)"); break;
7317 }
7318 } else {
7319 switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
7320 case Res_value::COMPLEX_UNIT_FRACTION: printf("%%"); break;
7321 case Res_value::COMPLEX_UNIT_FRACTION_PARENT: printf("%%p"); break;
7322 default: printf(" (unknown unit)"); break;
7323 }
7324 }
7325 }
7326
7327 // Normalize a string for output
normalizeForOutput(const char * input)7328 String8 ResTable::normalizeForOutput( const char *input )
7329 {
7330 String8 ret;
7331 char buff[2];
7332 buff[1] = '\0';
7333
7334 while (*input != '\0') {
7335 switch (*input) {
7336 // All interesting characters are in the ASCII zone, so we are making our own lives
7337 // easier by scanning the string one byte at a time.
7338 case '\\':
7339 ret += "\\\\";
7340 break;
7341 case '\n':
7342 ret += "\\n";
7343 break;
7344 case '"':
7345 ret += "\\\"";
7346 break;
7347 default:
7348 buff[0] = *input;
7349 ret += buff;
7350 break;
7351 }
7352
7353 input++;
7354 }
7355
7356 return ret;
7357 }
7358
print_value(const Package * pkg,const Res_value & value) const7359 void ResTable::print_value(const Package* pkg, const Res_value& value) const
7360 {
7361 if (value.dataType == Res_value::TYPE_NULL) {
7362 if (value.data == Res_value::DATA_NULL_UNDEFINED) {
7363 printf("(null)\n");
7364 } else if (value.data == Res_value::DATA_NULL_EMPTY) {
7365 printf("(null empty)\n");
7366 } else {
7367 // This should never happen.
7368 printf("(null) 0x%08x\n", value.data);
7369 }
7370 } else if (value.dataType == Res_value::TYPE_REFERENCE) {
7371 printf("(reference) 0x%08x\n", value.data);
7372 } else if (value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE) {
7373 printf("(dynamic reference) 0x%08x\n", value.data);
7374 } else if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
7375 printf("(attribute) 0x%08x\n", value.data);
7376 } else if (value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
7377 printf("(dynamic attribute) 0x%08x\n", value.data);
7378 } else if (value.dataType == Res_value::TYPE_STRING) {
7379 size_t len;
7380 const char* str8 = pkg->header->values.string8At(
7381 value.data, &len);
7382 if (str8 != NULL) {
7383 printf("(string8) \"%s\"\n", normalizeForOutput(str8).string());
7384 } else {
7385 const char16_t* str16 = pkg->header->values.stringAt(
7386 value.data, &len);
7387 if (str16 != NULL) {
7388 printf("(string16) \"%s\"\n",
7389 normalizeForOutput(String8(str16, len).string()).string());
7390 } else {
7391 printf("(string) null\n");
7392 }
7393 }
7394 } else if (value.dataType == Res_value::TYPE_FLOAT) {
7395 printf("(float) %g\n", *(const float*)&value.data);
7396 } else if (value.dataType == Res_value::TYPE_DIMENSION) {
7397 printf("(dimension) ");
7398 print_complex(value.data, false);
7399 printf("\n");
7400 } else if (value.dataType == Res_value::TYPE_FRACTION) {
7401 printf("(fraction) ");
7402 print_complex(value.data, true);
7403 printf("\n");
7404 } else if (value.dataType >= Res_value::TYPE_FIRST_COLOR_INT
7405 && value.dataType <= Res_value::TYPE_LAST_COLOR_INT) {
7406 printf("(color) #%08x\n", value.data);
7407 } else if (value.dataType == Res_value::TYPE_INT_BOOLEAN) {
7408 printf("(boolean) %s\n", value.data ? "true" : "false");
7409 } else if (value.dataType >= Res_value::TYPE_FIRST_INT
7410 && value.dataType <= Res_value::TYPE_LAST_INT) {
7411 printf("(int) 0x%08x or %d\n", value.data, value.data);
7412 } else {
7413 printf("(unknown type) t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)\n",
7414 (int)value.dataType, (int)value.data,
7415 (int)value.size, (int)value.res0);
7416 }
7417 }
7418
print(bool inclValues) const7419 void ResTable::print(bool inclValues) const
7420 {
7421 if (mError != 0) {
7422 printf("mError=0x%x (%s)\n", mError, strerror(mError));
7423 }
7424 size_t pgCount = mPackageGroups.size();
7425 printf("Package Groups (%d)\n", (int)pgCount);
7426 for (size_t pgIndex=0; pgIndex<pgCount; pgIndex++) {
7427 const PackageGroup* pg = mPackageGroups[pgIndex];
7428 printf("Package Group %d id=0x%02x packageCount=%d name=%s\n",
7429 (int)pgIndex, pg->id, (int)pg->packages.size(),
7430 String8(pg->name).string());
7431
7432 const KeyedVector<String16, uint8_t>& refEntries = pg->dynamicRefTable.entries();
7433 const size_t refEntryCount = refEntries.size();
7434 if (refEntryCount > 0) {
7435 printf(" DynamicRefTable entryCount=%d:\n", (int) refEntryCount);
7436 for (size_t refIndex = 0; refIndex < refEntryCount; refIndex++) {
7437 printf(" 0x%02x -> %s\n",
7438 refEntries.valueAt(refIndex),
7439 String8(refEntries.keyAt(refIndex)).string());
7440 }
7441 printf("\n");
7442 }
7443
7444 // Determine the number of resource splits for the resource types in this package.
7445 // It needs to be done outside of the loop below so all of the information for a
7446 // is displayed in a single block. Otherwise, a resource split's resource types
7447 // would be interleaved with other splits.
7448 size_t splitCount = 0;
7449 for (size_t typeIndex = 0; typeIndex < pg->types.size(); typeIndex++) {
7450 splitCount = max(splitCount, pg->types[typeIndex].size());
7451 }
7452
7453 int packageId = pg->id;
7454 for (size_t splitIndex = 0; splitIndex < splitCount; splitIndex++) {
7455 size_t pkgCount = pg->packages.size();
7456 for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
7457 const Package* pkg = pg->packages[pkgIndex];
7458 // Use a package's real ID, since the ID may have been assigned
7459 // if this package is a shared library.
7460 packageId = pkg->package->id;
7461 char16_t tmpName[sizeof(pkg->package->name)/sizeof(pkg->package->name[0])];
7462 strcpy16_dtoh(tmpName, pkg->package->name,
7463 sizeof(pkg->package->name)/sizeof(pkg->package->name[0]));
7464 printf(" Package %d id=0x%02x name=%s\n", (int)pkgIndex,
7465 pkg->package->id, String8(tmpName).string());
7466 }
7467
7468 for (size_t typeIndex = 0; typeIndex < pg->types.size(); typeIndex++) {
7469 const TypeList& typeList = pg->types[typeIndex];
7470 if (splitIndex >= typeList.size() || typeList.isEmpty()) {
7471 // Only dump if the split exists and contains entries for this type
7472 continue;
7473 }
7474 const Type* typeConfigs = typeList[splitIndex];
7475 const size_t NTC = typeConfigs->configs.size();
7476 printf(" type %d configCount=%d entryCount=%d\n",
7477 (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount);
7478 if (typeConfigs->typeSpecFlags != NULL) {
7479 for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) {
7480 uint32_t resID = (0xff000000 & ((packageId)<<24))
7481 | (0x00ff0000 & ((typeIndex+1)<<16))
7482 | (0x0000ffff & (entryIndex));
7483 // Since we are creating resID without actually
7484 // iterating over them, we have no idea which is a
7485 // dynamic reference. We must check.
7486 if (packageId == 0) {
7487 pg->dynamicRefTable.lookupResourceId(&resID);
7488 }
7489
7490 resource_name resName;
7491 if (this->getResourceName(resID, true, &resName)) {
7492 String8 type8;
7493 String8 name8;
7494 if (resName.type8 != NULL) {
7495 type8 = String8(resName.type8, resName.typeLen);
7496 } else {
7497 type8 = String8(resName.type, resName.typeLen);
7498 }
7499 if (resName.name8 != NULL) {
7500 name8 = String8(resName.name8, resName.nameLen);
7501 } else {
7502 name8 = String8(resName.name, resName.nameLen);
7503 }
7504 printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
7505 resID,
7506 CHAR16_TO_CSTR(resName.package, resName.packageLen),
7507 type8.string(), name8.string(),
7508 dtohl(typeConfigs->typeSpecFlags[entryIndex]));
7509 } else {
7510 printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
7511 }
7512 }
7513 }
7514 for (size_t configIndex=0; configIndex<NTC; configIndex++) {
7515 const ResTable_type* type = typeConfigs->configs[configIndex];
7516 if ((((uint64_t)type)&0x3) != 0) {
7517 printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type);
7518 continue;
7519 }
7520
7521 // Always copy the config, as fields get added and we need to
7522 // set the defaults.
7523 ResTable_config thisConfig;
7524 thisConfig.copyFromDtoH(type->config);
7525
7526 String8 configStr = thisConfig.toString();
7527 printf(" config %s", configStr.size() > 0
7528 ? configStr.string() : "(default)");
7529 if (type->flags != 0u) {
7530 printf(" flags=0x%02x", type->flags);
7531 if (type->flags & ResTable_type::FLAG_SPARSE) {
7532 printf(" [sparse]");
7533 }
7534 }
7535
7536 printf(":\n");
7537
7538 size_t entryCount = dtohl(type->entryCount);
7539 uint32_t entriesStart = dtohl(type->entriesStart);
7540 if ((entriesStart&0x3) != 0) {
7541 printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n",
7542 entriesStart);
7543 continue;
7544 }
7545 uint32_t typeSize = dtohl(type->header.size);
7546 if ((typeSize&0x3) != 0) {
7547 printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
7548 continue;
7549 }
7550
7551 const uint32_t* const eindex = (const uint32_t*)
7552 (((const uint8_t*)type) + dtohs(type->header.headerSize));
7553 for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
7554 size_t entryId;
7555 uint32_t thisOffset;
7556 if (type->flags & ResTable_type::FLAG_SPARSE) {
7557 const ResTable_sparseTypeEntry* entry =
7558 reinterpret_cast<const ResTable_sparseTypeEntry*>(
7559 eindex + entryIndex);
7560 entryId = dtohs(entry->idx);
7561 // Offsets are encoded as divided by 4.
7562 thisOffset = static_cast<uint32_t>(dtohs(entry->offset)) * 4u;
7563 } else {
7564 entryId = entryIndex;
7565 thisOffset = dtohl(eindex[entryIndex]);
7566 if (thisOffset == ResTable_type::NO_ENTRY) {
7567 continue;
7568 }
7569 }
7570
7571 uint32_t resID = (0xff000000 & ((packageId)<<24))
7572 | (0x00ff0000 & ((typeIndex+1)<<16))
7573 | (0x0000ffff & (entryId));
7574 if (packageId == 0) {
7575 pg->dynamicRefTable.lookupResourceId(&resID);
7576 }
7577 resource_name resName;
7578 if (this->getResourceName(resID, true, &resName)) {
7579 String8 type8;
7580 String8 name8;
7581 if (resName.type8 != NULL) {
7582 type8 = String8(resName.type8, resName.typeLen);
7583 } else {
7584 type8 = String8(resName.type, resName.typeLen);
7585 }
7586 if (resName.name8 != NULL) {
7587 name8 = String8(resName.name8, resName.nameLen);
7588 } else {
7589 name8 = String8(resName.name, resName.nameLen);
7590 }
7591 printf(" resource 0x%08x %s:%s/%s: ", resID,
7592 CHAR16_TO_CSTR(resName.package, resName.packageLen),
7593 type8.string(), name8.string());
7594 } else {
7595 printf(" INVALID RESOURCE 0x%08x: ", resID);
7596 }
7597 if ((thisOffset&0x3) != 0) {
7598 printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset);
7599 continue;
7600 }
7601 if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
7602 printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n",
7603 entriesStart, thisOffset, typeSize);
7604 continue;
7605 }
7606
7607 const ResTable_entry* ent = (const ResTable_entry*)
7608 (((const uint8_t*)type) + entriesStart + thisOffset);
7609 if (((entriesStart + thisOffset)&0x3) != 0) {
7610 printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n",
7611 (entriesStart + thisOffset));
7612 continue;
7613 }
7614
7615 uintptr_t esize = dtohs(ent->size);
7616 if ((esize&0x3) != 0) {
7617 printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
7618 continue;
7619 }
7620 if ((thisOffset+esize) > typeSize) {
7621 printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n",
7622 entriesStart, thisOffset, (void *)esize, typeSize);
7623 continue;
7624 }
7625
7626 const Res_value* valuePtr = NULL;
7627 const ResTable_map_entry* bagPtr = NULL;
7628 Res_value value;
7629 if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
7630 printf("<bag>");
7631 bagPtr = (const ResTable_map_entry*)ent;
7632 } else {
7633 valuePtr = (const Res_value*)
7634 (((const uint8_t*)ent) + esize);
7635 value.copyFrom_dtoh(*valuePtr);
7636 printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
7637 (int)value.dataType, (int)value.data,
7638 (int)value.size, (int)value.res0);
7639 }
7640
7641 if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
7642 printf(" (PUBLIC)");
7643 }
7644 printf("\n");
7645
7646 if (inclValues) {
7647 if (valuePtr != NULL) {
7648 printf(" ");
7649 print_value(typeConfigs->package, value);
7650 } else if (bagPtr != NULL) {
7651 const int N = dtohl(bagPtr->count);
7652 const uint8_t* baseMapPtr = (const uint8_t*)ent;
7653 size_t mapOffset = esize;
7654 const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
7655 const uint32_t parent = dtohl(bagPtr->parent.ident);
7656 uint32_t resolvedParent = parent;
7657 if (Res_GETPACKAGE(resolvedParent) + 1 == 0) {
7658 status_t err =
7659 pg->dynamicRefTable.lookupResourceId(&resolvedParent);
7660 if (err != NO_ERROR) {
7661 resolvedParent = 0;
7662 }
7663 }
7664 printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n",
7665 parent, resolvedParent, N);
7666 for (int i=0;
7667 i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
7668 printf(" #%i (Key=0x%08x): ",
7669 i, dtohl(mapPtr->name.ident));
7670 value.copyFrom_dtoh(mapPtr->value);
7671 print_value(typeConfigs->package, value);
7672 const size_t size = dtohs(mapPtr->value.size);
7673 mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
7674 mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
7675 }
7676 }
7677 }
7678 }
7679 }
7680 }
7681 }
7682 }
7683 }
7684
7685 } // namespace android
7686