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