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