blob: 57aaf24ec947e2a5fc2ed4394ceaebfe0b94f3ba [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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 <utils/Atomic.h>
21#include <utils/ByteOrder.h>
22#include <utils/Debug.h>
23#include <utils/ResourceTypes.h>
24#include <utils/String16.h>
25#include <utils/String8.h>
26#include <utils/TextOutput.h>
27#include <utils/Log.h>
28
29#include <stdlib.h>
30#include <string.h>
31#include <memory.h>
32#include <ctype.h>
33#include <stdint.h>
34
35#ifndef INT32_MAX
36#define INT32_MAX ((int32_t)(2147483647))
37#endif
38
39#define POOL_NOISY(x) //x
40#define XML_NOISY(x) //x
41#define TABLE_NOISY(x) //x
42#define TABLE_GETENTRY(x) //x
43#define TABLE_SUPER_NOISY(x) //x
44#define LOAD_TABLE_NOISY(x) //x
Dianne Hackbornb8d81672009-11-20 14:26:42 -080045#define TABLE_THEME(x) //x
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046
47namespace android {
48
49#ifdef HAVE_WINSOCK
50#undef nhtol
51#undef htonl
52
53#ifdef HAVE_LITTLE_ENDIAN
54#define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) )
55#define htonl(x) ntohl(x)
56#define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) )
57#define htons(x) ntohs(x)
58#else
59#define ntohl(x) (x)
60#define htonl(x) (x)
61#define ntohs(x) (x)
62#define htons(x) (x)
63#endif
64#endif
65
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +010066#define IDMAP_MAGIC 0x706d6469
67// size measured in sizeof(uint32_t)
68#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t))
69
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070static void printToLogFunc(void* cookie, const char* txt)
71{
72 LOGV("%s", txt);
73}
74
75// Standard C isspace() is only required to look at the low byte of its input, so
76// produces incorrect results for UTF-16 characters. For safety's sake, assume that
77// any high-byte UTF-16 code point is not whitespace.
78inline int isspace16(char16_t c) {
79 return (c < 0x0080 && isspace(c));
80}
81
82// range checked; guaranteed to NUL-terminate within the stated number of available slots
83// NOTE: if this truncates the dst string due to running out of space, no attempt is
84// made to avoid splitting surrogate pairs.
85static void strcpy16_dtoh(uint16_t* dst, const uint16_t* src, size_t avail)
86{
87 uint16_t* last = dst + avail - 1;
88 while (*src && (dst < last)) {
89 char16_t s = dtohs(*src);
90 *dst++ = s;
91 src++;
92 }
93 *dst = 0;
94}
95
96static status_t validate_chunk(const ResChunk_header* chunk,
97 size_t minSize,
98 const uint8_t* dataEnd,
99 const char* name)
100{
101 const uint16_t headerSize = dtohs(chunk->headerSize);
102 const uint32_t size = dtohl(chunk->size);
103
104 if (headerSize >= minSize) {
105 if (headerSize <= size) {
106 if (((headerSize|size)&0x3) == 0) {
107 if ((ssize_t)size <= (dataEnd-((const uint8_t*)chunk))) {
108 return NO_ERROR;
109 }
110 LOGW("%s data size %p extends beyond resource end %p.",
111 name, (void*)size,
112 (void*)(dataEnd-((const uint8_t*)chunk)));
113 return BAD_TYPE;
114 }
115 LOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.",
116 name, (int)size, (int)headerSize);
117 return BAD_TYPE;
118 }
119 LOGW("%s size %p is smaller than header size %p.",
120 name, (void*)size, (void*)(int)headerSize);
121 return BAD_TYPE;
122 }
123 LOGW("%s header size %p is too small.",
124 name, (void*)(int)headerSize);
125 return BAD_TYPE;
126}
127
128inline void Res_value::copyFrom_dtoh(const Res_value& src)
129{
130 size = dtohs(src.size);
131 res0 = src.res0;
132 dataType = src.dataType;
133 data = dtohl(src.data);
134}
135
136void Res_png_9patch::deviceToFile()
137{
138 for (int i = 0; i < numXDivs; i++) {
139 xDivs[i] = htonl(xDivs[i]);
140 }
141 for (int i = 0; i < numYDivs; i++) {
142 yDivs[i] = htonl(yDivs[i]);
143 }
144 paddingLeft = htonl(paddingLeft);
145 paddingRight = htonl(paddingRight);
146 paddingTop = htonl(paddingTop);
147 paddingBottom = htonl(paddingBottom);
148 for (int i=0; i<numColors; i++) {
149 colors[i] = htonl(colors[i]);
150 }
151}
152
153void Res_png_9patch::fileToDevice()
154{
155 for (int i = 0; i < numXDivs; i++) {
156 xDivs[i] = ntohl(xDivs[i]);
157 }
158 for (int i = 0; i < numYDivs; i++) {
159 yDivs[i] = ntohl(yDivs[i]);
160 }
161 paddingLeft = ntohl(paddingLeft);
162 paddingRight = ntohl(paddingRight);
163 paddingTop = ntohl(paddingTop);
164 paddingBottom = ntohl(paddingBottom);
165 for (int i=0; i<numColors; i++) {
166 colors[i] = ntohl(colors[i]);
167 }
168}
169
170size_t Res_png_9patch::serializedSize()
171{
172 // The size of this struct is 32 bytes on the 32-bit target system
173 // 4 * int8_t
174 // 4 * int32_t
175 // 3 * pointer
176 return 32
177 + numXDivs * sizeof(int32_t)
178 + numYDivs * sizeof(int32_t)
179 + numColors * sizeof(uint32_t);
180}
181
182void* Res_png_9patch::serialize()
183{
The Android Open Source Project4df24232009-03-05 14:34:35 -0800184 // Use calloc since we're going to leave a few holes in the data
185 // and want this to run cleanly under valgrind
186 void* newData = calloc(1, serializedSize());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 serialize(newData);
188 return newData;
189}
190
191void Res_png_9patch::serialize(void * outData)
192{
193 char* data = (char*) outData;
194 memmove(data, &wasDeserialized, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors
195 memmove(data + 12, &paddingLeft, 16); // copy paddingXXXX
196 data += 32;
197
198 memmove(data, this->xDivs, numXDivs * sizeof(int32_t));
199 data += numXDivs * sizeof(int32_t);
200 memmove(data, this->yDivs, numYDivs * sizeof(int32_t));
201 data += numYDivs * sizeof(int32_t);
202 memmove(data, this->colors, numColors * sizeof(uint32_t));
203}
204
205static void deserializeInternal(const void* inData, Res_png_9patch* outData) {
206 char* patch = (char*) inData;
207 if (inData != outData) {
208 memmove(&outData->wasDeserialized, patch, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors
209 memmove(&outData->paddingLeft, patch + 12, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors
210 }
211 outData->wasDeserialized = true;
212 char* data = (char*)outData;
213 data += sizeof(Res_png_9patch);
214 outData->xDivs = (int32_t*) data;
215 data += outData->numXDivs * sizeof(int32_t);
216 outData->yDivs = (int32_t*) data;
217 data += outData->numYDivs * sizeof(int32_t);
218 outData->colors = (uint32_t*) data;
219}
220
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +0100221static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes)
222{
223 if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) {
224 LOGW("idmap assertion failed: size=%d bytes\n", sizeBytes);
225 return false;
226 }
227 if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess
228 LOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n",
229 *map, htodl(IDMAP_MAGIC));
230 return false;
231 }
232 return true;
233}
234
235static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, uint32_t* outValue)
236{
237 // see README for details on the format of map
238 if (!assertIdmapHeader(map, sizeBytes)) {
239 return UNKNOWN_ERROR;
240 }
241 map = map + IDMAP_HEADER_SIZE; // skip ahead to data segment
242 // size of data block, in uint32_t
243 const size_t size = (sizeBytes - ResTable::IDMAP_HEADER_SIZE_BYTES) / sizeof(uint32_t);
244 const uint32_t type = Res_GETTYPE(key) + 1; // add one, idmap stores "public" type id
245 const uint32_t entry = Res_GETENTRY(key);
246 const uint32_t typeCount = *map;
247
248 if (type > typeCount) {
249 LOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount);
250 return UNKNOWN_ERROR;
251 }
252 if (typeCount > size) {
253 LOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, size);
254 return UNKNOWN_ERROR;
255 }
256 const uint32_t typeOffset = map[type];
257 if (typeOffset == 0) {
258 *outValue = 0;
259 return NO_ERROR;
260 }
261 if (typeOffset + 1 > size) {
262 LOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n",
263 typeOffset, size);
264 return UNKNOWN_ERROR;
265 }
266 const uint32_t entryCount = map[typeOffset];
267 const uint32_t entryOffset = map[typeOffset + 1];
268 if (entryCount == 0 || entry < entryOffset || entry - entryOffset > entryCount - 1) {
269 *outValue = 0;
270 return NO_ERROR;
271 }
272 const uint32_t index = typeOffset + 2 + entry - entryOffset;
273 if (index > size) {
274 LOGW("Resource ID map: entry index=%d exceeds size of map=%d\n", index, size);
275 *outValue = 0;
276 return NO_ERROR;
277 }
278 *outValue = map[index];
279
280 return NO_ERROR;
281}
282
283static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t *outId)
284{
285 if (!assertIdmapHeader(map, mapSize)) {
286 return UNKNOWN_ERROR;
287 }
288 const uint32_t* p = map + IDMAP_HEADER_SIZE + 1;
289 while (*p == 0) {
290 ++p;
291 }
292 *outId = (map[*p + IDMAP_HEADER_SIZE + 2] >> 24) & 0x000000ff;
293 return NO_ERROR;
294}
295
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296Res_png_9patch* Res_png_9patch::deserialize(const void* inData)
297{
298 if (sizeof(void*) != sizeof(int32_t)) {
299 LOGE("Cannot deserialize on non 32-bit system\n");
300 return NULL;
301 }
302 deserializeInternal(inData, (Res_png_9patch*) inData);
303 return (Res_png_9patch*) inData;
304}
305
306// --------------------------------------------------------------------
307// --------------------------------------------------------------------
308// --------------------------------------------------------------------
309
310ResStringPool::ResStringPool()
Kenny Root19138462009-12-04 09:38:48 -0800311 : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312{
313}
314
315ResStringPool::ResStringPool(const void* data, size_t size, bool copyData)
Kenny Root19138462009-12-04 09:38:48 -0800316 : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317{
318 setTo(data, size, copyData);
319}
320
321ResStringPool::~ResStringPool()
322{
323 uninit();
324}
325
326status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
327{
328 if (!data || !size) {
329 return (mError=BAD_TYPE);
330 }
331
332 uninit();
333
334 const bool notDeviceEndian = htods(0xf0) != 0xf0;
335
336 if (copyData || notDeviceEndian) {
337 mOwnedData = malloc(size);
338 if (mOwnedData == NULL) {
339 return (mError=NO_MEMORY);
340 }
341 memcpy(mOwnedData, data, size);
342 data = mOwnedData;
343 }
344
345 mHeader = (const ResStringPool_header*)data;
346
347 if (notDeviceEndian) {
348 ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader);
349 h->header.headerSize = dtohs(mHeader->header.headerSize);
350 h->header.type = dtohs(mHeader->header.type);
351 h->header.size = dtohl(mHeader->header.size);
352 h->stringCount = dtohl(mHeader->stringCount);
353 h->styleCount = dtohl(mHeader->styleCount);
354 h->flags = dtohl(mHeader->flags);
355 h->stringsStart = dtohl(mHeader->stringsStart);
356 h->stylesStart = dtohl(mHeader->stylesStart);
357 }
358
359 if (mHeader->header.headerSize > mHeader->header.size
360 || mHeader->header.size > size) {
361 LOGW("Bad string block: header size %d or total size %d is larger than data size %d\n",
362 (int)mHeader->header.headerSize, (int)mHeader->header.size, (int)size);
363 return (mError=BAD_TYPE);
364 }
365 mSize = mHeader->header.size;
366 mEntries = (const uint32_t*)
367 (((const uint8_t*)data)+mHeader->header.headerSize);
368
369 if (mHeader->stringCount > 0) {
370 if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow?
371 || (mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t)))
372 > size) {
373 LOGW("Bad string block: entry of %d items extends past data size %d\n",
374 (int)(mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))),
375 (int)size);
376 return (mError=BAD_TYPE);
377 }
Kenny Root19138462009-12-04 09:38:48 -0800378
379 size_t charSize;
380 if (mHeader->flags&ResStringPool_header::UTF8_FLAG) {
381 charSize = sizeof(uint8_t);
382 mCache = (char16_t**)malloc(sizeof(char16_t**)*mHeader->stringCount);
383 memset(mCache, 0, sizeof(char16_t**)*mHeader->stringCount);
384 } else {
385 charSize = sizeof(char16_t);
386 }
387
388 mStrings = (const void*)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 (((const uint8_t*)data)+mHeader->stringsStart);
390 if (mHeader->stringsStart >= (mHeader->header.size-sizeof(uint16_t))) {
391 LOGW("Bad string block: string pool starts at %d, after total size %d\n",
392 (int)mHeader->stringsStart, (int)mHeader->header.size);
393 return (mError=BAD_TYPE);
394 }
395 if (mHeader->styleCount == 0) {
396 mStringPoolSize =
Kenny Root19138462009-12-04 09:38:48 -0800397 (mHeader->header.size-mHeader->stringsStart)/charSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 } else {
399 // check invariant: styles follow the strings
400 if (mHeader->stylesStart <= mHeader->stringsStart) {
401 LOGW("Bad style block: style block starts at %d, before strings at %d\n",
402 (int)mHeader->stylesStart, (int)mHeader->stringsStart);
403 return (mError=BAD_TYPE);
404 }
405 mStringPoolSize =
Kenny Root19138462009-12-04 09:38:48 -0800406 (mHeader->stylesStart-mHeader->stringsStart)/charSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 }
408
409 // check invariant: stringCount > 0 requires a string pool to exist
410 if (mStringPoolSize == 0) {
411 LOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount);
412 return (mError=BAD_TYPE);
413 }
414
415 if (notDeviceEndian) {
416 size_t i;
417 uint32_t* e = const_cast<uint32_t*>(mEntries);
418 for (i=0; i<mHeader->stringCount; i++) {
419 e[i] = dtohl(mEntries[i]);
420 }
Kenny Root19138462009-12-04 09:38:48 -0800421 if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) {
422 const char16_t* strings = (const char16_t*)mStrings;
423 char16_t* s = const_cast<char16_t*>(strings);
424 for (i=0; i<mStringPoolSize; i++) {
425 s[i] = dtohs(strings[i]);
426 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 }
428 }
429
Kenny Root19138462009-12-04 09:38:48 -0800430 if ((mHeader->flags&ResStringPool_header::UTF8_FLAG &&
431 ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) ||
432 (!mHeader->flags&ResStringPool_header::UTF8_FLAG &&
433 ((char16_t*)mStrings)[mStringPoolSize-1] != 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 LOGW("Bad string block: last string is not 0-terminated\n");
435 return (mError=BAD_TYPE);
436 }
437 } else {
438 mStrings = NULL;
439 mStringPoolSize = 0;
440 }
441
442 if (mHeader->styleCount > 0) {
443 mEntryStyles = mEntries + mHeader->stringCount;
444 // invariant: integer overflow in calculating mEntryStyles
445 if (mEntryStyles < mEntries) {
446 LOGW("Bad string block: integer overflow finding styles\n");
447 return (mError=BAD_TYPE);
448 }
449
450 if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) {
451 LOGW("Bad string block: entry of %d styles extends past data size %d\n",
452 (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader),
453 (int)size);
454 return (mError=BAD_TYPE);
455 }
456 mStyles = (const uint32_t*)
457 (((const uint8_t*)data)+mHeader->stylesStart);
458 if (mHeader->stylesStart >= mHeader->header.size) {
459 LOGW("Bad string block: style pool starts %d, after total size %d\n",
460 (int)mHeader->stylesStart, (int)mHeader->header.size);
461 return (mError=BAD_TYPE);
462 }
463 mStylePoolSize =
464 (mHeader->header.size-mHeader->stylesStart)/sizeof(uint32_t);
465
466 if (notDeviceEndian) {
467 size_t i;
468 uint32_t* e = const_cast<uint32_t*>(mEntryStyles);
469 for (i=0; i<mHeader->styleCount; i++) {
470 e[i] = dtohl(mEntryStyles[i]);
471 }
472 uint32_t* s = const_cast<uint32_t*>(mStyles);
473 for (i=0; i<mStylePoolSize; i++) {
474 s[i] = dtohl(mStyles[i]);
475 }
476 }
477
478 const ResStringPool_span endSpan = {
479 { htodl(ResStringPool_span::END) },
480 htodl(ResStringPool_span::END), htodl(ResStringPool_span::END)
481 };
482 if (memcmp(&mStyles[mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t))],
483 &endSpan, sizeof(endSpan)) != 0) {
484 LOGW("Bad string block: last style is not 0xFFFFFFFF-terminated\n");
485 return (mError=BAD_TYPE);
486 }
487 } else {
488 mEntryStyles = NULL;
489 mStyles = NULL;
490 mStylePoolSize = 0;
491 }
492
493 return (mError=NO_ERROR);
494}
495
496status_t ResStringPool::getError() const
497{
498 return mError;
499}
500
501void ResStringPool::uninit()
502{
503 mError = NO_INIT;
504 if (mOwnedData) {
505 free(mOwnedData);
506 mOwnedData = NULL;
507 }
Kenny Root19138462009-12-04 09:38:48 -0800508 if (mHeader != NULL && mCache != NULL) {
509 for (size_t x = 0; x < mHeader->stringCount; x++) {
510 if (mCache[x] != NULL) {
511 free(mCache[x]);
512 mCache[x] = NULL;
513 }
514 }
515 free(mCache);
516 mCache = NULL;
517 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518}
519
Kenny Root19138462009-12-04 09:38:48 -0800520#define DECODE_LENGTH(str, chrsz, len) \
521 len = *(str); \
522 if (*(str)&(1<<(chrsz*8-1))) { \
523 (str)++; \
524 len = (((len)&((1<<(chrsz*8-1))-1))<<(chrsz*8)) + *(str); \
525 } \
526 (str)++;
527
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const
529{
530 if (mError == NO_ERROR && idx < mHeader->stringCount) {
Kenny Root19138462009-12-04 09:38:48 -0800531 const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0;
532 const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 if (off < (mStringPoolSize-1)) {
Kenny Root19138462009-12-04 09:38:48 -0800534 if (!isUTF8) {
535 const char16_t* strings = (char16_t*)mStrings;
536 const char16_t* str = strings+off;
537 DECODE_LENGTH(str, sizeof(char16_t), *outLen)
538 if ((uint32_t)(str+*outLen-strings) < mStringPoolSize) {
539 return str;
540 } else {
541 LOGW("Bad string block: string #%d extends to %d, past end at %d\n",
542 (int)idx, (int)(str+*outLen-strings), (int)mStringPoolSize);
543 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 } else {
Kenny Root19138462009-12-04 09:38:48 -0800545 const uint8_t* strings = (uint8_t*)mStrings;
546 const uint8_t* str = strings+off;
547 DECODE_LENGTH(str, sizeof(uint8_t), *outLen)
548 size_t encLen;
549 DECODE_LENGTH(str, sizeof(uint8_t), encLen)
550 if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
551 AutoMutex lock(mDecodeLock);
552 if (mCache[idx] != NULL) {
553 return mCache[idx];
554 }
555 char16_t *u16str = (char16_t *)calloc(*outLen+1, sizeof(char16_t));
556 if (!u16str) {
557 LOGW("No memory when trying to allocate decode cache for string #%d\n",
558 (int)idx);
559 return NULL;
560 }
561 const unsigned char *u8src = reinterpret_cast<const unsigned char *>(str);
562 utf8_to_utf16(u8src, encLen, u16str, *outLen);
563 mCache[idx] = u16str;
564 return u16str;
565 } else {
566 LOGW("Bad string block: string #%d extends to %d, past end at %d\n",
567 (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
568 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 }
570 } else {
571 LOGW("Bad string block: string #%d entry is at %d, past end at %d\n",
572 (int)idx, (int)(off*sizeof(uint16_t)),
573 (int)(mStringPoolSize*sizeof(uint16_t)));
574 }
575 }
576 return NULL;
577}
578
Kenny Root780d2a12010-02-22 22:36:26 -0800579const char* ResStringPool::string8At(size_t idx, size_t* outLen) const
580{
581 if (mError == NO_ERROR && idx < mHeader->stringCount) {
582 const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0;
583 const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t));
584 if (off < (mStringPoolSize-1)) {
585 if (isUTF8) {
586 const uint8_t* strings = (uint8_t*)mStrings;
587 const uint8_t* str = strings+off;
588 DECODE_LENGTH(str, sizeof(uint8_t), *outLen)
589 size_t encLen;
590 DECODE_LENGTH(str, sizeof(uint8_t), encLen)
591 if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
592 return (const char*)str;
593 } else {
594 LOGW("Bad string block: string #%d extends to %d, past end at %d\n",
595 (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
596 }
597 }
598 } else {
599 LOGW("Bad string block: string #%d entry is at %d, past end at %d\n",
600 (int)idx, (int)(off*sizeof(uint16_t)),
601 (int)(mStringPoolSize*sizeof(uint16_t)));
602 }
603 }
604 return NULL;
605}
606
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const
608{
609 return styleAt(ref.index);
610}
611
612const ResStringPool_span* ResStringPool::styleAt(size_t idx) const
613{
614 if (mError == NO_ERROR && idx < mHeader->styleCount) {
615 const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t));
616 if (off < mStylePoolSize) {
617 return (const ResStringPool_span*)(mStyles+off);
618 } else {
619 LOGW("Bad string block: style #%d entry is at %d, past end at %d\n",
620 (int)idx, (int)(off*sizeof(uint32_t)),
621 (int)(mStylePoolSize*sizeof(uint32_t)));
622 }
623 }
624 return NULL;
625}
626
627ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
628{
629 if (mError != NO_ERROR) {
630 return mError;
631 }
632
633 size_t len;
634
Kenny Root19138462009-12-04 09:38:48 -0800635 // TODO optimize searching for UTF-8 strings taking into account
636 // the cache fill to determine when to convert the searched-for
637 // string key to UTF-8.
638
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 if (mHeader->flags&ResStringPool_header::SORTED_FLAG) {
640 // Do a binary search for the string...
641 ssize_t l = 0;
642 ssize_t h = mHeader->stringCount-1;
643
644 ssize_t mid;
645 while (l <= h) {
646 mid = l + (h - l)/2;
647 const char16_t* s = stringAt(mid, &len);
648 int c = s ? strzcmp16(s, len, str, strLen) : -1;
649 POOL_NOISY(printf("Looking for %s, at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
650 String8(str).string(),
651 String8(s).string(),
652 c, (int)l, (int)mid, (int)h));
653 if (c == 0) {
654 return mid;
655 } else if (c < 0) {
656 l = mid + 1;
657 } else {
658 h = mid - 1;
659 }
660 }
661 } else {
662 // It is unusual to get the ID from an unsorted string block...
663 // most often this happens because we want to get IDs for style
664 // span tags; since those always appear at the end of the string
665 // block, start searching at the back.
666 for (int i=mHeader->stringCount-1; i>=0; i--) {
667 const char16_t* s = stringAt(i, &len);
668 POOL_NOISY(printf("Looking for %s, at %s, i=%d\n",
669 String8(str, strLen).string(),
670 String8(s).string(),
671 i));
672 if (s && strzcmp16(s, len, str, strLen) == 0) {
673 return i;
674 }
675 }
676 }
677
678 return NAME_NOT_FOUND;
679}
680
681size_t ResStringPool::size() const
682{
683 return (mError == NO_ERROR) ? mHeader->stringCount : 0;
684}
685
Kenny Rootbb79f642009-12-10 14:20:15 -0800686#ifndef HAVE_ANDROID_OS
687bool ResStringPool::isUTF8() const
688{
689 return (mHeader->flags&ResStringPool_header::UTF8_FLAG)!=0;
690}
691#endif
692
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693// --------------------------------------------------------------------
694// --------------------------------------------------------------------
695// --------------------------------------------------------------------
696
697ResXMLParser::ResXMLParser(const ResXMLTree& tree)
698 : mTree(tree), mEventCode(BAD_DOCUMENT)
699{
700}
701
702void ResXMLParser::restart()
703{
704 mCurNode = NULL;
705 mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT;
706}
Dianne Hackborncf244ad2010-03-09 15:00:30 -0800707const ResStringPool& ResXMLParser::getStrings() const
708{
709 return mTree.mStrings;
710}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800711
712ResXMLParser::event_code_t ResXMLParser::getEventType() const
713{
714 return mEventCode;
715}
716
717ResXMLParser::event_code_t ResXMLParser::next()
718{
719 if (mEventCode == START_DOCUMENT) {
720 mCurNode = mTree.mRootNode;
721 mCurExt = mTree.mRootExt;
722 return (mEventCode=mTree.mRootCode);
723 } else if (mEventCode >= FIRST_CHUNK_CODE) {
724 return nextNode();
725 }
726 return mEventCode;
727}
728
Mathias Agopian5f910972009-06-22 02:35:32 -0700729int32_t ResXMLParser::getCommentID() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730{
731 return mCurNode != NULL ? dtohl(mCurNode->comment.index) : -1;
732}
733
734const uint16_t* ResXMLParser::getComment(size_t* outLen) const
735{
736 int32_t id = getCommentID();
737 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
738}
739
Mathias Agopian5f910972009-06-22 02:35:32 -0700740uint32_t ResXMLParser::getLineNumber() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741{
742 return mCurNode != NULL ? dtohl(mCurNode->lineNumber) : -1;
743}
744
Mathias Agopian5f910972009-06-22 02:35:32 -0700745int32_t ResXMLParser::getTextID() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746{
747 if (mEventCode == TEXT) {
748 return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index);
749 }
750 return -1;
751}
752
753const uint16_t* ResXMLParser::getText(size_t* outLen) const
754{
755 int32_t id = getTextID();
756 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
757}
758
759ssize_t ResXMLParser::getTextValue(Res_value* outValue) const
760{
761 if (mEventCode == TEXT) {
762 outValue->copyFrom_dtoh(((const ResXMLTree_cdataExt*)mCurExt)->typedData);
763 return sizeof(Res_value);
764 }
765 return BAD_TYPE;
766}
767
Mathias Agopian5f910972009-06-22 02:35:32 -0700768int32_t ResXMLParser::getNamespacePrefixID() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769{
770 if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) {
771 return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->prefix.index);
772 }
773 return -1;
774}
775
776const uint16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const
777{
778 int32_t id = getNamespacePrefixID();
779 //printf("prefix=%d event=%p\n", id, mEventCode);
780 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
781}
782
Mathias Agopian5f910972009-06-22 02:35:32 -0700783int32_t ResXMLParser::getNamespaceUriID() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784{
785 if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) {
786 return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->uri.index);
787 }
788 return -1;
789}
790
791const uint16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const
792{
793 int32_t id = getNamespaceUriID();
794 //printf("uri=%d event=%p\n", id, mEventCode);
795 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
796}
797
Mathias Agopian5f910972009-06-22 02:35:32 -0700798int32_t ResXMLParser::getElementNamespaceID() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799{
800 if (mEventCode == START_TAG) {
801 return dtohl(((const ResXMLTree_attrExt*)mCurExt)->ns.index);
802 }
803 if (mEventCode == END_TAG) {
804 return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->ns.index);
805 }
806 return -1;
807}
808
809const uint16_t* ResXMLParser::getElementNamespace(size_t* outLen) const
810{
811 int32_t id = getElementNamespaceID();
812 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
813}
814
Mathias Agopian5f910972009-06-22 02:35:32 -0700815int32_t ResXMLParser::getElementNameID() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816{
817 if (mEventCode == START_TAG) {
818 return dtohl(((const ResXMLTree_attrExt*)mCurExt)->name.index);
819 }
820 if (mEventCode == END_TAG) {
821 return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->name.index);
822 }
823 return -1;
824}
825
826const uint16_t* ResXMLParser::getElementName(size_t* outLen) const
827{
828 int32_t id = getElementNameID();
829 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
830}
831
832size_t ResXMLParser::getAttributeCount() const
833{
834 if (mEventCode == START_TAG) {
835 return dtohs(((const ResXMLTree_attrExt*)mCurExt)->attributeCount);
836 }
837 return 0;
838}
839
Mathias Agopian5f910972009-06-22 02:35:32 -0700840int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841{
842 if (mEventCode == START_TAG) {
843 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
844 if (idx < dtohs(tag->attributeCount)) {
845 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
846 (((const uint8_t*)tag)
847 + dtohs(tag->attributeStart)
848 + (dtohs(tag->attributeSize)*idx));
849 return dtohl(attr->ns.index);
850 }
851 }
852 return -2;
853}
854
855const uint16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) const
856{
857 int32_t id = getAttributeNamespaceID(idx);
858 //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode);
859 //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id));
860 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
861}
862
Mathias Agopian5f910972009-06-22 02:35:32 -0700863int32_t ResXMLParser::getAttributeNameID(size_t idx) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864{
865 if (mEventCode == START_TAG) {
866 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
867 if (idx < dtohs(tag->attributeCount)) {
868 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
869 (((const uint8_t*)tag)
870 + dtohs(tag->attributeStart)
871 + (dtohs(tag->attributeSize)*idx));
872 return dtohl(attr->name.index);
873 }
874 }
875 return -1;
876}
877
878const uint16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const
879{
880 int32_t id = getAttributeNameID(idx);
881 //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode);
882 //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id));
883 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
884}
885
Mathias Agopian5f910972009-06-22 02:35:32 -0700886uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887{
888 int32_t id = getAttributeNameID(idx);
889 if (id >= 0 && (size_t)id < mTree.mNumResIds) {
890 return dtohl(mTree.mResIds[id]);
891 }
892 return 0;
893}
894
Mathias Agopian5f910972009-06-22 02:35:32 -0700895int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896{
897 if (mEventCode == START_TAG) {
898 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
899 if (idx < dtohs(tag->attributeCount)) {
900 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
901 (((const uint8_t*)tag)
902 + dtohs(tag->attributeStart)
903 + (dtohs(tag->attributeSize)*idx));
904 return dtohl(attr->rawValue.index);
905 }
906 }
907 return -1;
908}
909
910const uint16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen) const
911{
912 int32_t id = getAttributeValueStringID(idx);
913 //XML_NOISY(printf("getAttributeValue 0x%x=0x%x\n", idx, id));
914 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
915}
916
917int32_t ResXMLParser::getAttributeDataType(size_t idx) const
918{
919 if (mEventCode == START_TAG) {
920 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
921 if (idx < dtohs(tag->attributeCount)) {
922 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
923 (((const uint8_t*)tag)
924 + dtohs(tag->attributeStart)
925 + (dtohs(tag->attributeSize)*idx));
926 return attr->typedValue.dataType;
927 }
928 }
929 return Res_value::TYPE_NULL;
930}
931
932int32_t ResXMLParser::getAttributeData(size_t idx) const
933{
934 if (mEventCode == START_TAG) {
935 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
936 if (idx < dtohs(tag->attributeCount)) {
937 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
938 (((const uint8_t*)tag)
939 + dtohs(tag->attributeStart)
940 + (dtohs(tag->attributeSize)*idx));
941 return dtohl(attr->typedValue.data);
942 }
943 }
944 return 0;
945}
946
947ssize_t ResXMLParser::getAttributeValue(size_t idx, Res_value* outValue) const
948{
949 if (mEventCode == START_TAG) {
950 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
951 if (idx < dtohs(tag->attributeCount)) {
952 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
953 (((const uint8_t*)tag)
954 + dtohs(tag->attributeStart)
955 + (dtohs(tag->attributeSize)*idx));
956 outValue->copyFrom_dtoh(attr->typedValue);
957 return sizeof(Res_value);
958 }
959 }
960 return BAD_TYPE;
961}
962
963ssize_t ResXMLParser::indexOfAttribute(const char* ns, const char* attr) const
964{
965 String16 nsStr(ns != NULL ? ns : "");
966 String16 attrStr(attr);
967 return indexOfAttribute(ns ? nsStr.string() : NULL, ns ? nsStr.size() : 0,
968 attrStr.string(), attrStr.size());
969}
970
971ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen,
972 const char16_t* attr, size_t attrLen) const
973{
974 if (mEventCode == START_TAG) {
975 const size_t N = getAttributeCount();
976 for (size_t i=0; i<N; i++) {
977 size_t curNsLen, curAttrLen;
978 const char16_t* curNs = getAttributeNamespace(i, &curNsLen);
979 const char16_t* curAttr = getAttributeName(i, &curAttrLen);
980 //printf("%d: ns=%p attr=%p curNs=%p curAttr=%p\n",
981 // i, ns, attr, curNs, curAttr);
982 //printf(" --> attr=%s, curAttr=%s\n",
983 // String8(attr).string(), String8(curAttr).string());
984 if (attr && curAttr && (strzcmp16(attr, attrLen, curAttr, curAttrLen) == 0)) {
985 if (ns == NULL) {
986 if (curNs == NULL) return i;
987 } else if (curNs != NULL) {
988 //printf(" --> ns=%s, curNs=%s\n",
989 // String8(ns).string(), String8(curNs).string());
990 if (strzcmp16(ns, nsLen, curNs, curNsLen) == 0) return i;
991 }
992 }
993 }
994 }
995
996 return NAME_NOT_FOUND;
997}
998
999ssize_t ResXMLParser::indexOfID() const
1000{
1001 if (mEventCode == START_TAG) {
1002 const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->idIndex);
1003 if (idx > 0) return (idx-1);
1004 }
1005 return NAME_NOT_FOUND;
1006}
1007
1008ssize_t ResXMLParser::indexOfClass() const
1009{
1010 if (mEventCode == START_TAG) {
1011 const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->classIndex);
1012 if (idx > 0) return (idx-1);
1013 }
1014 return NAME_NOT_FOUND;
1015}
1016
1017ssize_t ResXMLParser::indexOfStyle() const
1018{
1019 if (mEventCode == START_TAG) {
1020 const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->styleIndex);
1021 if (idx > 0) return (idx-1);
1022 }
1023 return NAME_NOT_FOUND;
1024}
1025
1026ResXMLParser::event_code_t ResXMLParser::nextNode()
1027{
1028 if (mEventCode < 0) {
1029 return mEventCode;
1030 }
1031
1032 do {
1033 const ResXMLTree_node* next = (const ResXMLTree_node*)
1034 (((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size));
1035 //LOGW("Next node: prev=%p, next=%p\n", mCurNode, next);
1036
1037 if (((const uint8_t*)next) >= mTree.mDataEnd) {
1038 mCurNode = NULL;
1039 return (mEventCode=END_DOCUMENT);
1040 }
1041
1042 if (mTree.validateNode(next) != NO_ERROR) {
1043 mCurNode = NULL;
1044 return (mEventCode=BAD_DOCUMENT);
1045 }
1046
1047 mCurNode = next;
1048 const uint16_t headerSize = dtohs(next->header.headerSize);
1049 const uint32_t totalSize = dtohl(next->header.size);
1050 mCurExt = ((const uint8_t*)next) + headerSize;
1051 size_t minExtSize = 0;
1052 event_code_t eventCode = (event_code_t)dtohs(next->header.type);
1053 switch ((mEventCode=eventCode)) {
1054 case RES_XML_START_NAMESPACE_TYPE:
1055 case RES_XML_END_NAMESPACE_TYPE:
1056 minExtSize = sizeof(ResXMLTree_namespaceExt);
1057 break;
1058 case RES_XML_START_ELEMENT_TYPE:
1059 minExtSize = sizeof(ResXMLTree_attrExt);
1060 break;
1061 case RES_XML_END_ELEMENT_TYPE:
1062 minExtSize = sizeof(ResXMLTree_endElementExt);
1063 break;
1064 case RES_XML_CDATA_TYPE:
1065 minExtSize = sizeof(ResXMLTree_cdataExt);
1066 break;
1067 default:
1068 LOGW("Unknown XML block: header type %d in node at %d\n",
1069 (int)dtohs(next->header.type),
1070 (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)));
1071 continue;
1072 }
1073
1074 if ((totalSize-headerSize) < minExtSize) {
1075 LOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n",
1076 (int)dtohs(next->header.type),
1077 (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)),
1078 (int)(totalSize-headerSize), (int)minExtSize);
1079 return (mEventCode=BAD_DOCUMENT);
1080 }
1081
1082 //printf("CurNode=%p, CurExt=%p, headerSize=%d, minExtSize=%d\n",
1083 // mCurNode, mCurExt, headerSize, minExtSize);
1084
1085 return eventCode;
1086 } while (true);
1087}
1088
1089void ResXMLParser::getPosition(ResXMLParser::ResXMLPosition* pos) const
1090{
1091 pos->eventCode = mEventCode;
1092 pos->curNode = mCurNode;
1093 pos->curExt = mCurExt;
1094}
1095
1096void ResXMLParser::setPosition(const ResXMLParser::ResXMLPosition& pos)
1097{
1098 mEventCode = pos.eventCode;
1099 mCurNode = pos.curNode;
1100 mCurExt = pos.curExt;
1101}
1102
1103
1104// --------------------------------------------------------------------
1105
1106static volatile int32_t gCount = 0;
1107
1108ResXMLTree::ResXMLTree()
1109 : ResXMLParser(*this)
1110 , mError(NO_INIT), mOwnedData(NULL)
1111{
1112 //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1);
1113 restart();
1114}
1115
1116ResXMLTree::ResXMLTree(const void* data, size_t size, bool copyData)
1117 : ResXMLParser(*this)
1118 , mError(NO_INIT), mOwnedData(NULL)
1119{
1120 //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1);
1121 setTo(data, size, copyData);
1122}
1123
1124ResXMLTree::~ResXMLTree()
1125{
1126 //LOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1);
1127 uninit();
1128}
1129
1130status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData)
1131{
1132 uninit();
1133 mEventCode = START_DOCUMENT;
1134
1135 if (copyData) {
1136 mOwnedData = malloc(size);
1137 if (mOwnedData == NULL) {
1138 return (mError=NO_MEMORY);
1139 }
1140 memcpy(mOwnedData, data, size);
1141 data = mOwnedData;
1142 }
1143
1144 mHeader = (const ResXMLTree_header*)data;
1145 mSize = dtohl(mHeader->header.size);
1146 if (dtohs(mHeader->header.headerSize) > mSize || mSize > size) {
1147 LOGW("Bad XML block: header size %d or total size %d is larger than data size %d\n",
1148 (int)dtohs(mHeader->header.headerSize),
1149 (int)dtohl(mHeader->header.size), (int)size);
1150 mError = BAD_TYPE;
1151 restart();
1152 return mError;
1153 }
1154 mDataEnd = ((const uint8_t*)mHeader) + mSize;
1155
1156 mStrings.uninit();
1157 mRootNode = NULL;
1158 mResIds = NULL;
1159 mNumResIds = 0;
1160
1161 // First look for a couple interesting chunks: the string block
1162 // and first XML node.
1163 const ResChunk_header* chunk =
1164 (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize));
1165 const ResChunk_header* lastChunk = chunk;
1166 while (((const uint8_t*)chunk) < (mDataEnd-sizeof(ResChunk_header)) &&
1167 ((const uint8_t*)chunk) < (mDataEnd-dtohl(chunk->size))) {
1168 status_t err = validate_chunk(chunk, sizeof(ResChunk_header), mDataEnd, "XML");
1169 if (err != NO_ERROR) {
1170 mError = err;
1171 goto done;
1172 }
1173 const uint16_t type = dtohs(chunk->type);
1174 const size_t size = dtohl(chunk->size);
1175 XML_NOISY(printf("Scanning @ %p: type=0x%x, size=0x%x\n",
1176 (void*)(((uint32_t)chunk)-((uint32_t)mHeader)), type, size));
1177 if (type == RES_STRING_POOL_TYPE) {
1178 mStrings.setTo(chunk, size);
1179 } else if (type == RES_XML_RESOURCE_MAP_TYPE) {
1180 mResIds = (const uint32_t*)
1181 (((const uint8_t*)chunk)+dtohs(chunk->headerSize));
1182 mNumResIds = (dtohl(chunk->size)-dtohs(chunk->headerSize))/sizeof(uint32_t);
1183 } else if (type >= RES_XML_FIRST_CHUNK_TYPE
1184 && type <= RES_XML_LAST_CHUNK_TYPE) {
1185 if (validateNode((const ResXMLTree_node*)chunk) != NO_ERROR) {
1186 mError = BAD_TYPE;
1187 goto done;
1188 }
1189 mCurNode = (const ResXMLTree_node*)lastChunk;
1190 if (nextNode() == BAD_DOCUMENT) {
1191 mError = BAD_TYPE;
1192 goto done;
1193 }
1194 mRootNode = mCurNode;
1195 mRootExt = mCurExt;
1196 mRootCode = mEventCode;
1197 break;
1198 } else {
1199 XML_NOISY(printf("Skipping unknown chunk!\n"));
1200 }
1201 lastChunk = chunk;
1202 chunk = (const ResChunk_header*)
1203 (((const uint8_t*)chunk) + size);
1204 }
1205
1206 if (mRootNode == NULL) {
1207 LOGW("Bad XML block: no root element node found\n");
1208 mError = BAD_TYPE;
1209 goto done;
1210 }
1211
1212 mError = mStrings.getError();
1213
1214done:
1215 restart();
1216 return mError;
1217}
1218
1219status_t ResXMLTree::getError() const
1220{
1221 return mError;
1222}
1223
1224void ResXMLTree::uninit()
1225{
1226 mError = NO_INIT;
Kenny Root19138462009-12-04 09:38:48 -08001227 mStrings.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 if (mOwnedData) {
1229 free(mOwnedData);
1230 mOwnedData = NULL;
1231 }
1232 restart();
1233}
1234
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001235status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const
1236{
1237 const uint16_t eventCode = dtohs(node->header.type);
1238
1239 status_t err = validate_chunk(
1240 &node->header, sizeof(ResXMLTree_node),
1241 mDataEnd, "ResXMLTree_node");
1242
1243 if (err >= NO_ERROR) {
1244 // Only perform additional validation on START nodes
1245 if (eventCode != RES_XML_START_ELEMENT_TYPE) {
1246 return NO_ERROR;
1247 }
1248
1249 const uint16_t headerSize = dtohs(node->header.headerSize);
1250 const uint32_t size = dtohl(node->header.size);
1251 const ResXMLTree_attrExt* attrExt = (const ResXMLTree_attrExt*)
1252 (((const uint8_t*)node) + headerSize);
1253 // check for sensical values pulled out of the stream so far...
1254 if ((size >= headerSize + sizeof(ResXMLTree_attrExt))
1255 && ((void*)attrExt > (void*)node)) {
1256 const size_t attrSize = ((size_t)dtohs(attrExt->attributeSize))
1257 * dtohs(attrExt->attributeCount);
1258 if ((dtohs(attrExt->attributeStart)+attrSize) <= (size-headerSize)) {
1259 return NO_ERROR;
1260 }
1261 LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n",
1262 (unsigned int)(dtohs(attrExt->attributeStart)+attrSize),
1263 (unsigned int)(size-headerSize));
1264 }
1265 else {
1266 LOGW("Bad XML start block: node header size 0x%x, size 0x%x\n",
1267 (unsigned int)headerSize, (unsigned int)size);
1268 }
1269 return BAD_TYPE;
1270 }
1271
1272 return err;
1273
1274#if 0
1275 const bool isStart = dtohs(node->header.type) == RES_XML_START_ELEMENT_TYPE;
1276
1277 const uint16_t headerSize = dtohs(node->header.headerSize);
1278 const uint32_t size = dtohl(node->header.size);
1279
1280 if (headerSize >= (isStart ? sizeof(ResXMLTree_attrNode) : sizeof(ResXMLTree_node))) {
1281 if (size >= headerSize) {
1282 if (((const uint8_t*)node) <= (mDataEnd-size)) {
1283 if (!isStart) {
1284 return NO_ERROR;
1285 }
1286 if ((((size_t)dtohs(node->attributeSize))*dtohs(node->attributeCount))
1287 <= (size-headerSize)) {
1288 return NO_ERROR;
1289 }
1290 LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n",
1291 ((int)dtohs(node->attributeSize))*dtohs(node->attributeCount),
1292 (int)(size-headerSize));
1293 return BAD_TYPE;
1294 }
1295 LOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n",
1296 (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), (int)mSize);
1297 return BAD_TYPE;
1298 }
1299 LOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n",
1300 (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)),
1301 (int)headerSize, (int)size);
1302 return BAD_TYPE;
1303 }
1304 LOGW("Bad XML block: node at 0x%x header size 0x%x too small\n",
1305 (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)),
1306 (int)headerSize);
1307 return BAD_TYPE;
1308#endif
1309}
1310
1311// --------------------------------------------------------------------
1312// --------------------------------------------------------------------
1313// --------------------------------------------------------------------
1314
1315struct ResTable::Header
1316{
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01001317 Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL),
1318 resourceIDMap(NULL), resourceIDMapSize(0) { }
1319
1320 ~Header()
1321 {
1322 free(resourceIDMap);
1323 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324
Dianne Hackborn78c40512009-07-06 11:07:40 -07001325 ResTable* const owner;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001326 void* ownedData;
1327 const ResTable_header* header;
1328 size_t size;
1329 const uint8_t* dataEnd;
1330 size_t index;
1331 void* cookie;
1332
1333 ResStringPool values;
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01001334 uint32_t* resourceIDMap;
1335 size_t resourceIDMapSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336};
1337
1338struct ResTable::Type
1339{
1340 Type(const Header* _header, const Package* _package, size_t count)
1341 : header(_header), package(_package), entryCount(count),
1342 typeSpec(NULL), typeSpecFlags(NULL) { }
1343 const Header* const header;
1344 const Package* const package;
1345 const size_t entryCount;
1346 const ResTable_typeSpec* typeSpec;
1347 const uint32_t* typeSpecFlags;
1348 Vector<const ResTable_type*> configs;
1349};
1350
1351struct ResTable::Package
1352{
Dianne Hackborn78c40512009-07-06 11:07:40 -07001353 Package(ResTable* _owner, const Header* _header, const ResTable_package* _package)
1354 : owner(_owner), header(_header), package(_package) { }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001355 ~Package()
1356 {
1357 size_t i = types.size();
1358 while (i > 0) {
1359 i--;
1360 delete types[i];
1361 }
1362 }
1363
Dianne Hackborn78c40512009-07-06 11:07:40 -07001364 ResTable* const owner;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001365 const Header* const header;
1366 const ResTable_package* const package;
1367 Vector<Type*> types;
1368
Dianne Hackborn78c40512009-07-06 11:07:40 -07001369 ResStringPool typeStrings;
1370 ResStringPool keyStrings;
1371
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001372 const Type* getType(size_t idx) const {
1373 return idx < types.size() ? types[idx] : NULL;
1374 }
1375};
1376
1377// A group of objects describing a particular resource package.
1378// The first in 'package' is always the root object (from the resource
1379// table that defined the package); the ones after are skins on top of it.
1380struct ResTable::PackageGroup
1381{
Dianne Hackborn78c40512009-07-06 11:07:40 -07001382 PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id)
1383 : owner(_owner), name(_name), id(_id), typeCount(0), bags(NULL) { }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 ~PackageGroup() {
1385 clearBagCache();
1386 const size_t N = packages.size();
1387 for (size_t i=0; i<N; i++) {
Dianne Hackborn78c40512009-07-06 11:07:40 -07001388 Package* pkg = packages[i];
1389 if (pkg->owner == owner) {
1390 delete pkg;
1391 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001392 }
1393 }
1394
1395 void clearBagCache() {
1396 if (bags) {
1397 TABLE_NOISY(printf("bags=%p\n", bags));
1398 Package* pkg = packages[0];
1399 TABLE_NOISY(printf("typeCount=%x\n", typeCount));
1400 for (size_t i=0; i<typeCount; i++) {
1401 TABLE_NOISY(printf("type=%d\n", i));
1402 const Type* type = pkg->getType(i);
1403 if (type != NULL) {
1404 bag_set** typeBags = bags[i];
1405 TABLE_NOISY(printf("typeBags=%p\n", typeBags));
1406 if (typeBags) {
1407 TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount));
1408 const size_t N = type->entryCount;
1409 for (size_t j=0; j<N; j++) {
1410 if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF)
1411 free(typeBags[j]);
1412 }
1413 free(typeBags);
1414 }
1415 }
1416 }
1417 free(bags);
1418 bags = NULL;
1419 }
1420 }
1421
Dianne Hackborn78c40512009-07-06 11:07:40 -07001422 ResTable* const owner;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001423 String16 const name;
1424 uint32_t const id;
1425 Vector<Package*> packages;
Dianne Hackborn78c40512009-07-06 11:07:40 -07001426
1427 // This is for finding typeStrings and other common package stuff.
1428 Package* basePackage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001429
Dianne Hackborn78c40512009-07-06 11:07:40 -07001430 // For quick access.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001431 size_t typeCount;
Dianne Hackborn78c40512009-07-06 11:07:40 -07001432
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001433 // Computed attribute bags, first indexed by the type and second
1434 // by the entry in that type.
1435 bag_set*** bags;
1436};
1437
1438struct ResTable::bag_set
1439{
1440 size_t numAttrs; // number in array
1441 size_t availAttrs; // total space in array
1442 uint32_t typeSpecFlags;
1443 // Followed by 'numAttr' bag_entry structures.
1444};
1445
1446ResTable::Theme::Theme(const ResTable& table)
1447 : mTable(table)
1448{
1449 memset(mPackages, 0, sizeof(mPackages));
1450}
1451
1452ResTable::Theme::~Theme()
1453{
1454 for (size_t i=0; i<Res_MAXPACKAGE; i++) {
1455 package_info* pi = mPackages[i];
1456 if (pi != NULL) {
1457 free_package(pi);
1458 }
1459 }
1460}
1461
1462void ResTable::Theme::free_package(package_info* pi)
1463{
1464 for (size_t j=0; j<pi->numTypes; j++) {
1465 theme_entry* te = pi->types[j].entries;
1466 if (te != NULL) {
1467 free(te);
1468 }
1469 }
1470 free(pi);
1471}
1472
1473ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi)
1474{
1475 package_info* newpi = (package_info*)malloc(
1476 sizeof(package_info) + (pi->numTypes*sizeof(type_info)));
1477 newpi->numTypes = pi->numTypes;
1478 for (size_t j=0; j<newpi->numTypes; j++) {
1479 size_t cnt = pi->types[j].numEntries;
1480 newpi->types[j].numEntries = cnt;
1481 theme_entry* te = pi->types[j].entries;
1482 if (te != NULL) {
1483 theme_entry* newte = (theme_entry*)malloc(cnt*sizeof(theme_entry));
1484 newpi->types[j].entries = newte;
1485 memcpy(newte, te, cnt*sizeof(theme_entry));
1486 } else {
1487 newpi->types[j].entries = NULL;
1488 }
1489 }
1490 return newpi;
1491}
1492
1493status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
1494{
1495 const bag_entry* bag;
1496 uint32_t bagTypeSpecFlags = 0;
1497 mTable.lock();
1498 const ssize_t N = mTable.getBagLocked(resID, &bag, &bagTypeSpecFlags);
1499 TABLE_NOISY(LOGV("Applying style 0x%08x to theme %p, count=%d", resID, this, N));
1500 if (N < 0) {
1501 mTable.unlock();
1502 return N;
1503 }
1504
1505 uint32_t curPackage = 0xffffffff;
1506 ssize_t curPackageIndex = 0;
1507 package_info* curPI = NULL;
1508 uint32_t curType = 0xffffffff;
1509 size_t numEntries = 0;
1510 theme_entry* curEntries = NULL;
1511
1512 const bag_entry* end = bag + N;
1513 while (bag < end) {
1514 const uint32_t attrRes = bag->map.name.ident;
1515 const uint32_t p = Res_GETPACKAGE(attrRes);
1516 const uint32_t t = Res_GETTYPE(attrRes);
1517 const uint32_t e = Res_GETENTRY(attrRes);
1518
1519 if (curPackage != p) {
1520 const ssize_t pidx = mTable.getResourcePackageIndex(attrRes);
1521 if (pidx < 0) {
1522 LOGE("Style contains key with bad package: 0x%08x\n", attrRes);
1523 bag++;
1524 continue;
1525 }
1526 curPackage = p;
1527 curPackageIndex = pidx;
1528 curPI = mPackages[pidx];
1529 if (curPI == NULL) {
1530 PackageGroup* const grp = mTable.mPackageGroups[pidx];
1531 int cnt = grp->typeCount;
1532 curPI = (package_info*)malloc(
1533 sizeof(package_info) + (cnt*sizeof(type_info)));
1534 curPI->numTypes = cnt;
1535 memset(curPI->types, 0, cnt*sizeof(type_info));
1536 mPackages[pidx] = curPI;
1537 }
1538 curType = 0xffffffff;
1539 }
1540 if (curType != t) {
1541 if (t >= curPI->numTypes) {
1542 LOGE("Style contains key with bad type: 0x%08x\n", attrRes);
1543 bag++;
1544 continue;
1545 }
1546 curType = t;
1547 curEntries = curPI->types[t].entries;
1548 if (curEntries == NULL) {
1549 PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex];
1550 const Type* type = grp->packages[0]->getType(t);
1551 int cnt = type != NULL ? type->entryCount : 0;
1552 curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry));
1553 memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry));
1554 curPI->types[t].numEntries = cnt;
1555 curPI->types[t].entries = curEntries;
1556 }
1557 numEntries = curPI->types[t].numEntries;
1558 }
1559 if (e >= numEntries) {
1560 LOGE("Style contains key with bad entry: 0x%08x\n", attrRes);
1561 bag++;
1562 continue;
1563 }
1564 theme_entry* curEntry = curEntries + e;
1565 TABLE_NOISY(LOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x",
1566 attrRes, bag->map.value.dataType, bag->map.value.data,
1567 curEntry->value.dataType));
1568 if (force || curEntry->value.dataType == Res_value::TYPE_NULL) {
1569 curEntry->stringBlock = bag->stringBlock;
1570 curEntry->typeSpecFlags |= bagTypeSpecFlags;
1571 curEntry->value = bag->map.value;
1572 }
1573
1574 bag++;
1575 }
1576
1577 mTable.unlock();
1578
1579 //LOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this);
1580 //dumpToLog();
1581
1582 return NO_ERROR;
1583}
1584
1585status_t ResTable::Theme::setTo(const Theme& other)
1586{
1587 //LOGI("Setting theme %p from theme %p...\n", this, &other);
1588 //dumpToLog();
1589 //other.dumpToLog();
1590
1591 if (&mTable == &other.mTable) {
1592 for (size_t i=0; i<Res_MAXPACKAGE; i++) {
1593 if (mPackages[i] != NULL) {
1594 free_package(mPackages[i]);
1595 }
1596 if (other.mPackages[i] != NULL) {
1597 mPackages[i] = copy_package(other.mPackages[i]);
1598 } else {
1599 mPackages[i] = NULL;
1600 }
1601 }
1602 } else {
1603 // @todo: need to really implement this, not just copy
1604 // the system package (which is still wrong because it isn't
1605 // fixing up resource references).
1606 for (size_t i=0; i<Res_MAXPACKAGE; i++) {
1607 if (mPackages[i] != NULL) {
1608 free_package(mPackages[i]);
1609 }
1610 if (i == 0 && other.mPackages[i] != NULL) {
1611 mPackages[i] = copy_package(other.mPackages[i]);
1612 } else {
1613 mPackages[i] = NULL;
1614 }
1615 }
1616 }
1617
1618 //LOGI("Final theme:");
1619 //dumpToLog();
1620
1621 return NO_ERROR;
1622}
1623
1624ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
1625 uint32_t* outTypeSpecFlags) const
1626{
1627 int cnt = 20;
1628
1629 if (outTypeSpecFlags != NULL) *outTypeSpecFlags = 0;
1630
1631 do {
1632 const ssize_t p = mTable.getResourcePackageIndex(resID);
1633 const uint32_t t = Res_GETTYPE(resID);
1634 const uint32_t e = Res_GETENTRY(resID);
1635
Dianne Hackbornb8d81672009-11-20 14:26:42 -08001636 TABLE_THEME(LOGI("Looking up attr 0x%08x in theme %p", resID, this));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001637
1638 if (p >= 0) {
1639 const package_info* const pi = mPackages[p];
Dianne Hackbornb8d81672009-11-20 14:26:42 -08001640 TABLE_THEME(LOGI("Found package: %p", pi));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001641 if (pi != NULL) {
Dianne Hackbornb8d81672009-11-20 14:26:42 -08001642 TABLE_THEME(LOGI("Desired type index is %ld in avail %d", t, pi->numTypes));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001643 if (t < pi->numTypes) {
1644 const type_info& ti = pi->types[t];
Dianne Hackbornb8d81672009-11-20 14:26:42 -08001645 TABLE_THEME(LOGI("Desired entry index is %ld in avail %d", e, ti.numEntries));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001646 if (e < ti.numEntries) {
1647 const theme_entry& te = ti.entries[e];
Dianne Hackbornb8d81672009-11-20 14:26:42 -08001648 if (outTypeSpecFlags != NULL) {
1649 *outTypeSpecFlags |= te.typeSpecFlags;
1650 }
1651 TABLE_THEME(LOGI("Theme value: type=0x%x, data=0x%08x",
1652 te.value.dataType, te.value.data));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001653 const uint8_t type = te.value.dataType;
1654 if (type == Res_value::TYPE_ATTRIBUTE) {
1655 if (cnt > 0) {
1656 cnt--;
1657 resID = te.value.data;
1658 continue;
1659 }
1660 LOGW("Too many attribute references, stopped at: 0x%08x\n", resID);
1661 return BAD_INDEX;
1662 } else if (type != Res_value::TYPE_NULL) {
1663 *outValue = te.value;
1664 return te.stringBlock;
1665 }
1666 return BAD_INDEX;
1667 }
1668 }
1669 }
1670 }
1671 break;
1672
1673 } while (true);
1674
1675 return BAD_INDEX;
1676}
1677
1678ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue,
1679 ssize_t blockIndex, uint32_t* outLastRef,
Dianne Hackborn0d221012009-07-29 15:41:19 -07001680 uint32_t* inoutTypeSpecFlags, ResTable_config* inoutConfig) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001681{
1682 //printf("Resolving type=0x%x\n", inOutValue->dataType);
1683 if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) {
1684 uint32_t newTypeSpecFlags;
1685 blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags);
Dianne Hackbornb8d81672009-11-20 14:26:42 -08001686 TABLE_THEME(LOGI("Resolving attr reference: blockIndex=%d, type=0x%x, data=%p\n",
1687 (int)blockIndex, (int)inOutValue->dataType, (void*)inOutValue->data));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001688 if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags;
1689 //printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType);
1690 if (blockIndex < 0) {
1691 return blockIndex;
1692 }
1693 }
Dianne Hackborn0d221012009-07-29 15:41:19 -07001694 return mTable.resolveReference(inOutValue, blockIndex, outLastRef,
1695 inoutTypeSpecFlags, inoutConfig);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001696}
1697
1698void ResTable::Theme::dumpToLog() const
1699{
1700 LOGI("Theme %p:\n", this);
1701 for (size_t i=0; i<Res_MAXPACKAGE; i++) {
1702 package_info* pi = mPackages[i];
1703 if (pi == NULL) continue;
1704
1705 LOGI(" Package #0x%02x:\n", (int)(i+1));
1706 for (size_t j=0; j<pi->numTypes; j++) {
1707 type_info& ti = pi->types[j];
1708 if (ti.numEntries == 0) continue;
1709
1710 LOGI(" Type #0x%02x:\n", (int)(j+1));
1711 for (size_t k=0; k<ti.numEntries; k++) {
1712 theme_entry& te = ti.entries[k];
1713 if (te.value.dataType == Res_value::TYPE_NULL) continue;
1714 LOGI(" 0x%08x: t=0x%x, d=0x%08x (block=%d)\n",
1715 (int)Res_MAKEID(i, j, k),
1716 te.value.dataType, (int)te.value.data, (int)te.stringBlock);
1717 }
1718 }
1719 }
1720}
1721
1722ResTable::ResTable()
1723 : mError(NO_INIT)
1724{
1725 memset(&mParams, 0, sizeof(mParams));
1726 memset(mPackageMap, 0, sizeof(mPackageMap));
1727 //LOGI("Creating ResTable %p\n", this);
1728}
1729
1730ResTable::ResTable(const void* data, size_t size, void* cookie, bool copyData)
1731 : mError(NO_INIT)
1732{
1733 memset(&mParams, 0, sizeof(mParams));
1734 memset(mPackageMap, 0, sizeof(mPackageMap));
1735 add(data, size, cookie, copyData);
1736 LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
1737 //LOGI("Creating ResTable %p\n", this);
1738}
1739
1740ResTable::~ResTable()
1741{
1742 //LOGI("Destroying ResTable in %p\n", this);
1743 uninit();
1744}
1745
1746inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const
1747{
1748 return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1;
1749}
1750
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01001751status_t ResTable::add(const void* data, size_t size, void* cookie, bool copyData,
1752 const void* idmap)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001753{
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01001754 return add(data, size, cookie, NULL, copyData, reinterpret_cast<const Asset*>(idmap));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001755}
1756
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01001757status_t ResTable::add(Asset* asset, void* cookie, bool copyData, const void* idmap)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001758{
1759 const void* data = asset->getBuffer(true);
1760 if (data == NULL) {
1761 LOGW("Unable to get buffer of resource asset file");
1762 return UNKNOWN_ERROR;
1763 }
1764 size_t size = (size_t)asset->getLength();
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01001765 return add(data, size, cookie, asset, copyData, reinterpret_cast<const Asset*>(idmap));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001766}
1767
Dianne Hackborn78c40512009-07-06 11:07:40 -07001768status_t ResTable::add(ResTable* src)
1769{
1770 mError = src->mError;
Dianne Hackborn78c40512009-07-06 11:07:40 -07001771
1772 for (size_t i=0; i<src->mHeaders.size(); i++) {
1773 mHeaders.add(src->mHeaders[i]);
1774 }
1775
1776 for (size_t i=0; i<src->mPackageGroups.size(); i++) {
1777 PackageGroup* srcPg = src->mPackageGroups[i];
1778 PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id);
1779 for (size_t j=0; j<srcPg->packages.size(); j++) {
1780 pg->packages.add(srcPg->packages[j]);
1781 }
1782 pg->basePackage = srcPg->basePackage;
1783 pg->typeCount = srcPg->typeCount;
1784 mPackageGroups.add(pg);
1785 }
1786
1787 memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap));
1788
1789 return mError;
1790}
1791
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001792status_t ResTable::add(const void* data, size_t size, void* cookie,
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01001793 Asset* asset, bool copyData, const Asset* idmap)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001794{
1795 if (!data) return NO_ERROR;
Dianne Hackborn78c40512009-07-06 11:07:40 -07001796 Header* header = new Header(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001797 header->index = mHeaders.size();
1798 header->cookie = cookie;
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01001799 if (idmap != NULL) {
1800 const size_t idmap_size = idmap->getLength();
1801 const void* idmap_data = const_cast<Asset*>(idmap)->getBuffer(true);
1802 header->resourceIDMap = (uint32_t*)malloc(idmap_size);
1803 if (header->resourceIDMap == NULL) {
1804 delete header;
1805 return (mError = NO_MEMORY);
1806 }
1807 memcpy((void*)header->resourceIDMap, idmap_data, idmap_size);
1808 header->resourceIDMapSize = idmap_size;
1809 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001810 mHeaders.add(header);
1811
1812 const bool notDeviceEndian = htods(0xf0) != 0xf0;
1813
1814 LOAD_TABLE_NOISY(
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01001815 LOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d "
1816 "idmap=%p\n", data, size, cookie, asset, copyData, idmap));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001817
1818 if (copyData || notDeviceEndian) {
1819 header->ownedData = malloc(size);
1820 if (header->ownedData == NULL) {
1821 return (mError=NO_MEMORY);
1822 }
1823 memcpy(header->ownedData, data, size);
1824 data = header->ownedData;
1825 }
1826
1827 header->header = (const ResTable_header*)data;
1828 header->size = dtohl(header->header->header.size);
1829 //LOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size,
1830 // dtohl(header->header->header.size), header->header->header.size);
1831 LOAD_TABLE_NOISY(LOGV("Loading ResTable @%p:\n", header->header));
1832 LOAD_TABLE_NOISY(printHexData(2, header->header, header->size < 256 ? header->size : 256,
1833 16, 16, 0, false, printToLogFunc));
1834 if (dtohs(header->header->header.headerSize) > header->size
1835 || header->size > size) {
1836 LOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n",
1837 (int)dtohs(header->header->header.headerSize),
1838 (int)header->size, (int)size);
1839 return (mError=BAD_TYPE);
1840 }
1841 if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) {
1842 LOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n",
1843 (int)dtohs(header->header->header.headerSize),
1844 (int)header->size);
1845 return (mError=BAD_TYPE);
1846 }
1847 header->dataEnd = ((const uint8_t*)header->header) + header->size;
1848
1849 // Iterate through all chunks.
1850 size_t curPackage = 0;
1851
1852 const ResChunk_header* chunk =
1853 (const ResChunk_header*)(((const uint8_t*)header->header)
1854 + dtohs(header->header->header.headerSize));
1855 while (((const uint8_t*)chunk) <= (header->dataEnd-sizeof(ResChunk_header)) &&
1856 ((const uint8_t*)chunk) <= (header->dataEnd-dtohl(chunk->size))) {
1857 status_t err = validate_chunk(chunk, sizeof(ResChunk_header), header->dataEnd, "ResTable");
1858 if (err != NO_ERROR) {
1859 return (mError=err);
1860 }
1861 TABLE_NOISY(LOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n",
1862 dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size),
1863 (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))));
1864 const size_t csize = dtohl(chunk->size);
1865 const uint16_t ctype = dtohs(chunk->type);
1866 if (ctype == RES_STRING_POOL_TYPE) {
1867 if (header->values.getError() != NO_ERROR) {
1868 // Only use the first string chunk; ignore any others that
1869 // may appear.
1870 status_t err = header->values.setTo(chunk, csize);
1871 if (err != NO_ERROR) {
1872 return (mError=err);
1873 }
1874 } else {
1875 LOGW("Multiple string chunks found in resource table.");
1876 }
1877 } else if (ctype == RES_TABLE_PACKAGE_TYPE) {
1878 if (curPackage >= dtohl(header->header->packageCount)) {
1879 LOGW("More package chunks were found than the %d declared in the header.",
1880 dtohl(header->header->packageCount));
1881 return (mError=BAD_TYPE);
1882 }
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01001883 uint32_t idmap_id = 0;
1884 if (idmap != NULL) {
1885 uint32_t tmp;
1886 if (getIdmapPackageId(header->resourceIDMap,
1887 header->resourceIDMapSize,
1888 &tmp) == NO_ERROR) {
1889 idmap_id = tmp;
1890 }
1891 }
1892 if (parsePackage((ResTable_package*)chunk, header, idmap_id) != NO_ERROR) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001893 return mError;
1894 }
1895 curPackage++;
1896 } else {
1897 LOGW("Unknown chunk type %p in table at %p.\n",
1898 (void*)(int)(ctype),
1899 (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)));
1900 }
1901 chunk = (const ResChunk_header*)
1902 (((const uint8_t*)chunk) + csize);
1903 }
1904
1905 if (curPackage < dtohl(header->header->packageCount)) {
1906 LOGW("Fewer package chunks (%d) were found than the %d declared in the header.",
1907 (int)curPackage, dtohl(header->header->packageCount));
1908 return (mError=BAD_TYPE);
1909 }
1910 mError = header->values.getError();
1911 if (mError != NO_ERROR) {
1912 LOGW("No string values found in resource table!");
1913 }
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01001914
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001915 TABLE_NOISY(LOGV("Returning from add with mError=%d\n", mError));
1916 return mError;
1917}
1918
1919status_t ResTable::getError() const
1920{
1921 return mError;
1922}
1923
1924void ResTable::uninit()
1925{
1926 mError = NO_INIT;
1927 size_t N = mPackageGroups.size();
1928 for (size_t i=0; i<N; i++) {
1929 PackageGroup* g = mPackageGroups[i];
1930 delete g;
1931 }
1932 N = mHeaders.size();
1933 for (size_t i=0; i<N; i++) {
1934 Header* header = mHeaders[i];
Dianne Hackborn78c40512009-07-06 11:07:40 -07001935 if (header->owner == this) {
1936 if (header->ownedData) {
1937 free(header->ownedData);
1938 }
1939 delete header;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001940 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001941 }
1942
1943 mPackageGroups.clear();
1944 mHeaders.clear();
1945}
1946
1947bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const
1948{
1949 if (mError != NO_ERROR) {
1950 return false;
1951 }
1952
1953 const ssize_t p = getResourcePackageIndex(resID);
1954 const int t = Res_GETTYPE(resID);
1955 const int e = Res_GETENTRY(resID);
1956
1957 if (p < 0) {
Dianne Hackborn6cca1592009-09-20 12:40:03 -07001958 if (Res_GETPACKAGE(resID)+1 == 0) {
1959 LOGW("No package identifier when getting name for resource number 0x%08x", resID);
1960 } else {
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001961 LOGW("No known package when getting name for resource number 0x%08x", resID);
Dianne Hackborn6cca1592009-09-20 12:40:03 -07001962 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001963 return false;
1964 }
1965 if (t < 0) {
1966 LOGW("No type identifier when getting name for resource number 0x%08x", resID);
1967 return false;
1968 }
1969
1970 const PackageGroup* const grp = mPackageGroups[p];
1971 if (grp == NULL) {
1972 LOGW("Bad identifier when getting name for resource number 0x%08x", resID);
1973 return false;
1974 }
1975 if (grp->packages.size() > 0) {
1976 const Package* const package = grp->packages[0];
1977
1978 const ResTable_type* type;
1979 const ResTable_entry* entry;
1980 ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL);
1981 if (offset <= 0) {
1982 return false;
1983 }
1984
1985 outName->package = grp->name.string();
1986 outName->packageLen = grp->name.size();
Dianne Hackborn78c40512009-07-06 11:07:40 -07001987 outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen);
1988 outName->name = grp->basePackage->keyStrings.stringAt(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001989 dtohl(entry->key.index), &outName->nameLen);
1990 return true;
1991 }
1992
1993 return false;
1994}
1995
1996ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag,
1997 uint32_t* outSpecFlags, ResTable_config* outConfig) const
1998{
1999 if (mError != NO_ERROR) {
2000 return mError;
2001 }
2002
2003 const ssize_t p = getResourcePackageIndex(resID);
2004 const int t = Res_GETTYPE(resID);
2005 const int e = Res_GETENTRY(resID);
2006
2007 if (p < 0) {
Dianne Hackborn6cca1592009-09-20 12:40:03 -07002008 if (Res_GETPACKAGE(resID)+1 == 0) {
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08002009 LOGW("No package identifier when getting value for resource number 0x%08x", resID);
Dianne Hackborn6cca1592009-09-20 12:40:03 -07002010 } else {
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08002011 LOGW("No known package when getting value for resource number 0x%08x", resID);
Dianne Hackborn6cca1592009-09-20 12:40:03 -07002012 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002013 return BAD_INDEX;
2014 }
2015 if (t < 0) {
2016 LOGW("No type identifier when getting value for resource number 0x%08x", resID);
2017 return BAD_INDEX;
2018 }
2019
2020 const Res_value* bestValue = NULL;
2021 const Package* bestPackage = NULL;
2022 ResTable_config bestItem;
2023 memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up
2024
2025 if (outSpecFlags != NULL) *outSpecFlags = 0;
2026
2027 // Look through all resource packages, starting with the most
2028 // recently added.
2029 const PackageGroup* const grp = mPackageGroups[p];
2030 if (grp == NULL) {
2031 LOGW("Bad identifier when getting value for resource number 0x%08x", resID);
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08002032 return BAD_INDEX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002033 }
2034 size_t ip = grp->packages.size();
2035 while (ip > 0) {
2036 ip--;
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002037 int T = t;
2038 int E = e;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002039
2040 const Package* const package = grp->packages[ip];
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002041 if (package->header->resourceIDMap) {
2042 uint32_t overlayResID = 0x0;
2043 status_t retval = idmapLookup(package->header->resourceIDMap,
2044 package->header->resourceIDMapSize,
2045 resID, &overlayResID);
2046 if (retval == NO_ERROR && overlayResID != 0x0) {
2047 // for this loop iteration, this is the type and entry we really want
2048 LOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
2049 T = Res_GETTYPE(overlayResID);
2050 E = Res_GETENTRY(overlayResID);
2051 } else {
2052 // resource not present in overlay package, continue with the next package
2053 continue;
2054 }
2055 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002056
2057 const ResTable_type* type;
2058 const ResTable_entry* entry;
2059 const Type* typeClass;
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002060 ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002061 if (offset <= 0) {
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002062 // No {entry, appropriate config} pair found in package. If this
2063 // package is an overlay package (ip != 0), this simply means the
2064 // overlay package did not specify a default.
2065 // Non-overlay packages are still required to provide a default.
2066 if (offset < 0 && ip == 0) {
Kenny Root68c2e912010-09-02 14:58:47 -07002067 LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n",
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002068 resID, T, E, ip, (int)offset);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002069 return offset;
2070 }
2071 continue;
2072 }
2073
2074 if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) {
2075 if (!mayBeBag) {
2076 LOGW("Requesting resource %p failed because it is complex\n",
2077 (void*)resID);
2078 }
2079 continue;
2080 }
2081
2082 TABLE_NOISY(aout << "Resource type data: "
2083 << HexDump(type, dtohl(type->header.size)) << endl);
2084
2085 if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) {
2086 LOGW("ResTable_item at %d is beyond type chunk data %d",
2087 (int)offset, dtohl(type->header.size));
2088 return BAD_TYPE;
2089 }
2090
2091 const Res_value* item =
2092 (const Res_value*)(((const uint8_t*)type) + offset);
2093 ResTable_config thisConfig;
2094 thisConfig.copyFromDtoH(type->config);
2095
2096 if (outSpecFlags != NULL) {
2097 if (typeClass->typeSpecFlags != NULL) {
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002098 *outSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002099 } else {
2100 *outSpecFlags = -1;
2101 }
2102 }
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002103
2104 if (bestPackage != NULL &&
2105 (bestItem.isMoreSpecificThan(thisConfig) || bestItem.diff(thisConfig) == 0)) {
2106 // Discard thisConfig not only if bestItem is more specific, but also if the two configs
2107 // are identical (diff == 0), or overlay packages will not take effect.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002108 continue;
2109 }
2110
2111 bestItem = thisConfig;
2112 bestValue = item;
2113 bestPackage = package;
2114 }
2115
2116 TABLE_NOISY(printf("Found result: package %p\n", bestPackage));
2117
2118 if (bestValue) {
2119 outValue->size = dtohs(bestValue->size);
2120 outValue->res0 = bestValue->res0;
2121 outValue->dataType = bestValue->dataType;
2122 outValue->data = dtohl(bestValue->data);
2123 if (outConfig != NULL) {
2124 *outConfig = bestItem;
2125 }
2126 TABLE_NOISY(size_t len;
2127 printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n",
2128 bestPackage->header->index,
2129 outValue->dataType,
2130 outValue->dataType == bestValue->TYPE_STRING
2131 ? String8(bestPackage->header->values.stringAt(
2132 outValue->data, &len)).string()
2133 : "",
2134 outValue->data));
2135 return bestPackage->header->index;
2136 }
2137
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08002138 return BAD_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002139}
2140
2141ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
Dianne Hackborn0d221012009-07-29 15:41:19 -07002142 uint32_t* outLastRef, uint32_t* inoutTypeSpecFlags,
2143 ResTable_config* outConfig) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002144{
2145 int count=0;
2146 while (blockIndex >= 0 && value->dataType == value->TYPE_REFERENCE
2147 && value->data != 0 && count < 20) {
2148 if (outLastRef) *outLastRef = value->data;
2149 uint32_t lastRef = value->data;
2150 uint32_t newFlags = 0;
Dianne Hackborn0d221012009-07-29 15:41:19 -07002151 const ssize_t newIndex = getResource(value->data, value, true, &newFlags,
2152 outConfig);
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08002153 if (newIndex == BAD_INDEX) {
2154 return BAD_INDEX;
2155 }
Dianne Hackbornb8d81672009-11-20 14:26:42 -08002156 TABLE_THEME(LOGI("Resolving reference %p: newIndex=%d, type=0x%x, data=%p\n",
2157 (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002158 //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex);
2159 if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags;
2160 if (newIndex < 0) {
2161 // This can fail if the resource being referenced is a style...
2162 // in this case, just return the reference, and expect the
2163 // caller to deal with.
2164 return blockIndex;
2165 }
2166 blockIndex = newIndex;
2167 count++;
2168 }
2169 return blockIndex;
2170}
2171
2172const char16_t* ResTable::valueToString(
2173 const Res_value* value, size_t stringBlock,
2174 char16_t tmpBuffer[TMP_BUFFER_SIZE], size_t* outLen)
2175{
2176 if (!value) {
2177 return NULL;
2178 }
2179 if (value->dataType == value->TYPE_STRING) {
2180 return getTableStringBlock(stringBlock)->stringAt(value->data, outLen);
2181 }
2182 // XXX do int to string conversions.
2183 return NULL;
2184}
2185
2186ssize_t ResTable::lockBag(uint32_t resID, const bag_entry** outBag) const
2187{
2188 mLock.lock();
2189 ssize_t err = getBagLocked(resID, outBag);
2190 if (err < NO_ERROR) {
2191 //printf("*** get failed! unlocking\n");
2192 mLock.unlock();
2193 }
2194 return err;
2195}
2196
2197void ResTable::unlockBag(const bag_entry* bag) const
2198{
2199 //printf("<<< unlockBag %p\n", this);
2200 mLock.unlock();
2201}
2202
2203void ResTable::lock() const
2204{
2205 mLock.lock();
2206}
2207
2208void ResTable::unlock() const
2209{
2210 mLock.unlock();
2211}
2212
2213ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
2214 uint32_t* outTypeSpecFlags) const
2215{
2216 if (mError != NO_ERROR) {
2217 return mError;
2218 }
2219
2220 const ssize_t p = getResourcePackageIndex(resID);
2221 const int t = Res_GETTYPE(resID);
2222 const int e = Res_GETENTRY(resID);
2223
2224 if (p < 0) {
2225 LOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID);
2226 return BAD_INDEX;
2227 }
2228 if (t < 0) {
2229 LOGW("No type identifier when getting bag for resource number 0x%08x", resID);
2230 return BAD_INDEX;
2231 }
2232
2233 //printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t);
2234 PackageGroup* const grp = mPackageGroups[p];
2235 if (grp == NULL) {
2236 LOGW("Bad identifier when getting bag for resource number 0x%08x", resID);
2237 return false;
2238 }
2239
2240 if (t >= (int)grp->typeCount) {
2241 LOGW("Type identifier 0x%x is larger than type count 0x%x",
2242 t+1, (int)grp->typeCount);
2243 return BAD_INDEX;
2244 }
2245
2246 const Package* const basePackage = grp->packages[0];
2247
2248 const Type* const typeConfigs = basePackage->getType(t);
2249
2250 const size_t NENTRY = typeConfigs->entryCount;
2251 if (e >= (int)NENTRY) {
2252 LOGW("Entry identifier 0x%x is larger than entry count 0x%x",
2253 e, (int)typeConfigs->entryCount);
2254 return BAD_INDEX;
2255 }
2256
2257 // First see if we've already computed this bag...
2258 if (grp->bags) {
2259 bag_set** typeSet = grp->bags[t];
2260 if (typeSet) {
2261 bag_set* set = typeSet[e];
2262 if (set) {
2263 if (set != (bag_set*)0xFFFFFFFF) {
2264 if (outTypeSpecFlags != NULL) {
2265 *outTypeSpecFlags = set->typeSpecFlags;
2266 }
2267 *outBag = (bag_entry*)(set+1);
2268 //LOGI("Found existing bag for: %p\n", (void*)resID);
2269 return set->numAttrs;
2270 }
2271 LOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
2272 resID);
2273 return BAD_INDEX;
2274 }
2275 }
2276 }
2277
2278 // Bag not found, we need to compute it!
2279 if (!grp->bags) {
2280 grp->bags = (bag_set***)malloc(sizeof(bag_set*)*grp->typeCount);
2281 if (!grp->bags) return NO_MEMORY;
2282 memset(grp->bags, 0, sizeof(bag_set*)*grp->typeCount);
2283 }
2284
2285 bag_set** typeSet = grp->bags[t];
2286 if (!typeSet) {
2287 typeSet = (bag_set**)malloc(sizeof(bag_set*)*NENTRY);
2288 if (!typeSet) return NO_MEMORY;
2289 memset(typeSet, 0, sizeof(bag_set*)*NENTRY);
2290 grp->bags[t] = typeSet;
2291 }
2292
2293 // Mark that we are currently working on this one.
2294 typeSet[e] = (bag_set*)0xFFFFFFFF;
2295
2296 // This is what we are building.
2297 bag_set* set = NULL;
2298
2299 TABLE_NOISY(LOGI("Building bag: %p\n", (void*)resID));
2300
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002301 ResTable_config bestConfig;
2302 memset(&bestConfig, 0, sizeof(bestConfig));
2303
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002304 // Now collect all bag attributes from all packages.
2305 size_t ip = grp->packages.size();
2306 while (ip > 0) {
2307 ip--;
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002308 int T = t;
2309 int E = e;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002310
2311 const Package* const package = grp->packages[ip];
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002312 if (package->header->resourceIDMap) {
2313 uint32_t overlayResID = 0x0;
2314 status_t retval = idmapLookup(package->header->resourceIDMap,
2315 package->header->resourceIDMapSize,
2316 resID, &overlayResID);
2317 if (retval == NO_ERROR && overlayResID != 0x0) {
2318 // for this loop iteration, this is the type and entry we really want
2319 LOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
2320 T = Res_GETTYPE(overlayResID);
2321 E = Res_GETENTRY(overlayResID);
2322 } else {
2323 // resource not present in overlay package, continue with the next package
2324 continue;
2325 }
2326 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002327
2328 const ResTable_type* type;
2329 const ResTable_entry* entry;
2330 const Type* typeClass;
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002331 LOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E);
2332 ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002333 LOGV("Resulting offset=%d\n", offset);
2334 if (offset <= 0) {
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002335 // No {entry, appropriate config} pair found in package. If this
2336 // package is an overlay package (ip != 0), this simply means the
2337 // overlay package did not specify a default.
2338 // Non-overlay packages are still required to provide a default.
2339 if (offset < 0 && ip == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002340 if (set) free(set);
2341 return offset;
2342 }
2343 continue;
2344 }
2345
2346 if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) {
2347 LOGW("Skipping entry %p in package table %d because it is not complex!\n",
2348 (void*)resID, (int)ip);
2349 continue;
2350 }
2351
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002352 if (set != NULL && !type->config.isBetterThan(bestConfig, NULL)) {
2353 continue;
2354 }
2355 bestConfig = type->config;
2356 if (set) {
2357 free(set);
2358 set = NULL;
2359 }
2360
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002361 const uint16_t entrySize = dtohs(entry->size);
2362 const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
2363 ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0;
2364 const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
2365 ? dtohl(((const ResTable_map_entry*)entry)->count) : 0;
2366
2367 size_t N = count;
2368
2369 TABLE_NOISY(LOGI("Found map: size=%p parent=%p count=%d\n",
2370 entrySize, parent, count));
2371
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002372 // If this map inherits from another, we need to start
2373 // with its parent's values. Otherwise start out empty.
2374 TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n",
2375 entrySize, parent));
2376 if (parent) {
2377 const bag_entry* parentBag;
2378 uint32_t parentTypeSpecFlags = 0;
2379 const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags);
2380 const size_t NT = ((NP >= 0) ? NP : 0) + N;
2381 set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
2382 if (set == NULL) {
2383 return NO_MEMORY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002384 }
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002385 if (NP > 0) {
2386 memcpy(set+1, parentBag, NP*sizeof(bag_entry));
2387 set->numAttrs = NP;
2388 TABLE_NOISY(LOGI("Initialized new bag with %d inherited attributes.\n", NP));
2389 } else {
2390 TABLE_NOISY(LOGI("Initialized new bag with no inherited attributes.\n"));
2391 set->numAttrs = 0;
2392 }
2393 set->availAttrs = NT;
2394 set->typeSpecFlags = parentTypeSpecFlags;
2395 } else {
2396 set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
2397 if (set == NULL) {
2398 return NO_MEMORY;
2399 }
2400 set->numAttrs = 0;
2401 set->availAttrs = N;
2402 set->typeSpecFlags = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002403 }
2404
2405 if (typeClass->typeSpecFlags != NULL) {
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01002406 set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002407 } else {
2408 set->typeSpecFlags = -1;
2409 }
2410
2411 // Now merge in the new attributes...
2412 ssize_t curOff = offset;
2413 const ResTable_map* map;
2414 bag_entry* entries = (bag_entry*)(set+1);
2415 size_t curEntry = 0;
2416 uint32_t pos = 0;
2417 TABLE_NOISY(LOGI("Starting with set %p, entries=%p, avail=%d\n",
2418 set, entries, set->availAttrs));
2419 while (pos < count) {
2420 TABLE_NOISY(printf("Now at %p\n", (void*)curOff));
2421
2422 if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) {
2423 LOGW("ResTable_map at %d is beyond type chunk data %d",
2424 (int)curOff, dtohl(type->header.size));
2425 return BAD_TYPE;
2426 }
2427 map = (const ResTable_map*)(((const uint8_t*)type) + curOff);
2428 N++;
2429
2430 const uint32_t newName = htodl(map->name.ident);
2431 bool isInside;
2432 uint32_t oldName = 0;
2433 while ((isInside=(curEntry < set->numAttrs))
2434 && (oldName=entries[curEntry].map.name.ident) < newName) {
2435 TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n",
2436 curEntry, entries[curEntry].map.name.ident));
2437 curEntry++;
2438 }
2439
2440 if ((!isInside) || oldName != newName) {
2441 // This is a new attribute... figure out what to do with it.
2442 if (set->numAttrs >= set->availAttrs) {
2443 // Need to alloc more memory...
2444 const size_t newAvail = set->availAttrs+N;
2445 set = (bag_set*)realloc(set,
2446 sizeof(bag_set)
2447 + sizeof(bag_entry)*newAvail);
2448 if (set == NULL) {
2449 return NO_MEMORY;
2450 }
2451 set->availAttrs = newAvail;
2452 entries = (bag_entry*)(set+1);
2453 TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n",
2454 set, entries, set->availAttrs));
2455 }
2456 if (isInside) {
2457 // Going in the middle, need to make space.
2458 memmove(entries+curEntry+1, entries+curEntry,
2459 sizeof(bag_entry)*(set->numAttrs-curEntry));
2460 set->numAttrs++;
2461 }
2462 TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n",
2463 curEntry, newName));
2464 } else {
2465 TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n",
2466 curEntry, oldName));
2467 }
2468
2469 bag_entry* cur = entries+curEntry;
2470
2471 cur->stringBlock = package->header->index;
2472 cur->map.name.ident = newName;
2473 cur->map.value.copyFrom_dtoh(map->value);
2474 TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n",
2475 curEntry, cur, cur->stringBlock, cur->map.name.ident,
2476 cur->map.value.dataType, cur->map.value.data));
2477
2478 // On to the next!
2479 curEntry++;
2480 pos++;
2481 const size_t size = dtohs(map->value.size);
2482 curOff += size + sizeof(*map)-sizeof(map->value);
2483 };
2484 if (curEntry > set->numAttrs) {
2485 set->numAttrs = curEntry;
2486 }
2487 }
2488
2489 // And this is it...
2490 typeSet[e] = set;
2491 if (set) {
2492 if (outTypeSpecFlags != NULL) {
2493 *outTypeSpecFlags = set->typeSpecFlags;
2494 }
2495 *outBag = (bag_entry*)(set+1);
2496 TABLE_NOISY(LOGI("Returning %d attrs\n", set->numAttrs));
2497 return set->numAttrs;
2498 }
2499 return BAD_INDEX;
2500}
2501
2502void ResTable::setParameters(const ResTable_config* params)
2503{
2504 mLock.lock();
2505 TABLE_GETENTRY(LOGI("Setting parameters: imsi:%d/%d lang:%c%c cnt:%c%c "
2506 "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
2507 params->mcc, params->mnc,
2508 params->language[0] ? params->language[0] : '-',
2509 params->language[1] ? params->language[1] : '-',
2510 params->country[0] ? params->country[0] : '-',
2511 params->country[1] ? params->country[1] : '-',
2512 params->orientation,
2513 params->touchscreen,
2514 params->density,
2515 params->keyboard,
2516 params->inputFlags,
2517 params->navigation,
2518 params->screenWidth,
2519 params->screenHeight));
2520 mParams = *params;
2521 for (size_t i=0; i<mPackageGroups.size(); i++) {
2522 TABLE_NOISY(LOGI("CLEARING BAGS FOR GROUP %d!", i));
2523 mPackageGroups[i]->clearBagCache();
2524 }
2525 mLock.unlock();
2526}
2527
2528void ResTable::getParameters(ResTable_config* params) const
2529{
2530 mLock.lock();
2531 *params = mParams;
2532 mLock.unlock();
2533}
2534
2535struct id_name_map {
2536 uint32_t id;
2537 size_t len;
2538 char16_t name[6];
2539};
2540
2541const static id_name_map ID_NAMES[] = {
2542 { ResTable_map::ATTR_TYPE, 5, { '^', 't', 'y', 'p', 'e' } },
2543 { ResTable_map::ATTR_L10N, 5, { '^', 'l', '1', '0', 'n' } },
2544 { ResTable_map::ATTR_MIN, 4, { '^', 'm', 'i', 'n' } },
2545 { ResTable_map::ATTR_MAX, 4, { '^', 'm', 'a', 'x' } },
2546 { ResTable_map::ATTR_OTHER, 6, { '^', 'o', 't', 'h', 'e', 'r' } },
2547 { ResTable_map::ATTR_ZERO, 5, { '^', 'z', 'e', 'r', 'o' } },
2548 { ResTable_map::ATTR_ONE, 4, { '^', 'o', 'n', 'e' } },
2549 { ResTable_map::ATTR_TWO, 4, { '^', 't', 'w', 'o' } },
2550 { ResTable_map::ATTR_FEW, 4, { '^', 'f', 'e', 'w' } },
2551 { ResTable_map::ATTR_MANY, 5, { '^', 'm', 'a', 'n', 'y' } },
2552};
2553
2554uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen,
2555 const char16_t* type, size_t typeLen,
2556 const char16_t* package,
2557 size_t packageLen,
2558 uint32_t* outTypeSpecFlags) const
2559{
2560 TABLE_SUPER_NOISY(printf("Identifier for name: error=%d\n", mError));
2561
2562 // Check for internal resource identifier as the very first thing, so
2563 // that we will always find them even when there are no resources.
2564 if (name[0] == '^') {
2565 const int N = (sizeof(ID_NAMES)/sizeof(ID_NAMES[0]));
2566 size_t len;
2567 for (int i=0; i<N; i++) {
2568 const id_name_map* m = ID_NAMES + i;
2569 len = m->len;
2570 if (len != nameLen) {
2571 continue;
2572 }
2573 for (size_t j=1; j<len; j++) {
2574 if (m->name[j] != name[j]) {
2575 goto nope;
2576 }
2577 }
2578 return m->id;
2579nope:
2580 ;
2581 }
2582 if (nameLen > 7) {
2583 if (name[1] == 'i' && name[2] == 'n'
2584 && name[3] == 'd' && name[4] == 'e' && name[5] == 'x'
2585 && name[6] == '_') {
2586 int index = atoi(String8(name + 7, nameLen - 7).string());
2587 if (Res_CHECKID(index)) {
2588 LOGW("Array resource index: %d is too large.",
2589 index);
2590 return 0;
2591 }
2592 return Res_MAKEARRAY(index);
2593 }
2594 }
2595 return 0;
2596 }
2597
2598 if (mError != NO_ERROR) {
2599 return 0;
2600 }
2601
2602 // Figure out the package and type we are looking in...
2603
2604 const char16_t* packageEnd = NULL;
2605 const char16_t* typeEnd = NULL;
2606 const char16_t* const nameEnd = name+nameLen;
2607 const char16_t* p = name;
2608 while (p < nameEnd) {
2609 if (*p == ':') packageEnd = p;
2610 else if (*p == '/') typeEnd = p;
2611 p++;
2612 }
2613 if (*name == '@') name++;
2614 if (name >= nameEnd) {
2615 return 0;
2616 }
2617
2618 if (packageEnd) {
2619 package = name;
2620 packageLen = packageEnd-name;
2621 name = packageEnd+1;
2622 } else if (!package) {
2623 return 0;
2624 }
2625
2626 if (typeEnd) {
2627 type = name;
2628 typeLen = typeEnd-name;
2629 name = typeEnd+1;
2630 } else if (!type) {
2631 return 0;
2632 }
2633
2634 if (name >= nameEnd) {
2635 return 0;
2636 }
2637 nameLen = nameEnd-name;
2638
2639 TABLE_NOISY(printf("Looking for identifier: type=%s, name=%s, package=%s\n",
2640 String8(type, typeLen).string(),
2641 String8(name, nameLen).string(),
2642 String8(package, packageLen).string()));
2643
2644 const size_t NG = mPackageGroups.size();
2645 for (size_t ig=0; ig<NG; ig++) {
2646 const PackageGroup* group = mPackageGroups[ig];
2647
2648 if (strzcmp16(package, packageLen,
2649 group->name.string(), group->name.size())) {
2650 TABLE_NOISY(printf("Skipping package group: %s\n", String8(group->name).string()));
2651 continue;
2652 }
2653
Dianne Hackborn78c40512009-07-06 11:07:40 -07002654 const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002655 if (ti < 0) {
2656 TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string()));
2657 continue;
2658 }
2659
Dianne Hackborn78c40512009-07-06 11:07:40 -07002660 const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002661 if (ei < 0) {
2662 TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string()));
2663 continue;
2664 }
2665
2666 TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei));
2667
2668 const Type* const typeConfigs = group->packages[0]->getType(ti);
2669 if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) {
2670 TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n",
2671 String8(group->name).string(), ti));
2672 }
2673
2674 size_t NTC = typeConfigs->configs.size();
2675 for (size_t tci=0; tci<NTC; tci++) {
2676 const ResTable_type* const ty = typeConfigs->configs[tci];
2677 const uint32_t typeOffset = dtohl(ty->entriesStart);
2678
2679 const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size);
2680 const uint32_t* const eindex = (const uint32_t*)
2681 (((const uint8_t*)ty) + dtohs(ty->header.headerSize));
2682
2683 const size_t NE = dtohl(ty->entryCount);
2684 for (size_t i=0; i<NE; i++) {
2685 uint32_t offset = dtohl(eindex[i]);
2686 if (offset == ResTable_type::NO_ENTRY) {
2687 continue;
2688 }
2689
2690 offset += typeOffset;
2691
2692 if (offset > (dtohl(ty->header.size)-sizeof(ResTable_entry))) {
2693 LOGW("ResTable_entry at %d is beyond type chunk data %d",
2694 offset, dtohl(ty->header.size));
2695 return 0;
2696 }
2697 if ((offset&0x3) != 0) {
2698 LOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s",
2699 (int)offset, (int)group->id, (int)ti+1, (int)i,
2700 String8(package, packageLen).string(),
2701 String8(type, typeLen).string(),
2702 String8(name, nameLen).string());
2703 return 0;
2704 }
2705
2706 const ResTable_entry* const entry = (const ResTable_entry*)
2707 (((const uint8_t*)ty) + offset);
2708 if (dtohs(entry->size) < sizeof(*entry)) {
2709 LOGW("ResTable_entry size %d is too small", dtohs(entry->size));
2710 return BAD_TYPE;
2711 }
2712
2713 TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n",
2714 i, ei, dtohl(entry->key.index)));
2715 if (dtohl(entry->key.index) == (size_t)ei) {
2716 if (outTypeSpecFlags) {
2717 *outTypeSpecFlags = typeConfigs->typeSpecFlags[i];
2718 }
2719 return Res_MAKEID(group->id-1, ti, i);
2720 }
2721 }
2722 }
2723 }
2724
2725 return 0;
2726}
2727
2728bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen,
2729 String16* outPackage,
2730 String16* outType,
2731 String16* outName,
2732 const String16* defType,
2733 const String16* defPackage,
2734 const char** outErrorMsg)
2735{
2736 const char16_t* packageEnd = NULL;
2737 const char16_t* typeEnd = NULL;
2738 const char16_t* p = refStr;
2739 const char16_t* const end = p + refLen;
2740 while (p < end) {
2741 if (*p == ':') packageEnd = p;
2742 else if (*p == '/') {
2743 typeEnd = p;
2744 break;
2745 }
2746 p++;
2747 }
2748 p = refStr;
2749 if (*p == '@') p++;
2750
2751 if (packageEnd) {
2752 *outPackage = String16(p, packageEnd-p);
2753 p = packageEnd+1;
2754 } else {
2755 if (!defPackage) {
2756 if (outErrorMsg) {
2757 *outErrorMsg = "No resource package specified";
2758 }
2759 return false;
2760 }
2761 *outPackage = *defPackage;
2762 }
2763 if (typeEnd) {
2764 *outType = String16(p, typeEnd-p);
2765 p = typeEnd+1;
2766 } else {
2767 if (!defType) {
2768 if (outErrorMsg) {
2769 *outErrorMsg = "No resource type specified";
2770 }
2771 return false;
2772 }
2773 *outType = *defType;
2774 }
2775 *outName = String16(p, end-p);
2776 return true;
2777}
2778
2779static uint32_t get_hex(char c, bool* outError)
2780{
2781 if (c >= '0' && c <= '9') {
2782 return c - '0';
2783 } else if (c >= 'a' && c <= 'f') {
2784 return c - 'a' + 0xa;
2785 } else if (c >= 'A' && c <= 'F') {
2786 return c - 'A' + 0xa;
2787 }
2788 *outError = true;
2789 return 0;
2790}
2791
2792struct unit_entry
2793{
2794 const char* name;
2795 size_t len;
2796 uint8_t type;
2797 uint32_t unit;
2798 float scale;
2799};
2800
2801static const unit_entry unitNames[] = {
2802 { "px", strlen("px"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PX, 1.0f },
2803 { "dip", strlen("dip"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f },
2804 { "dp", strlen("dp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f },
2805 { "sp", strlen("sp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_SP, 1.0f },
2806 { "pt", strlen("pt"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PT, 1.0f },
2807 { "in", strlen("in"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_IN, 1.0f },
2808 { "mm", strlen("mm"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_MM, 1.0f },
2809 { "%", strlen("%"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION, 1.0f/100 },
2810 { "%p", strlen("%p"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100 },
2811 { NULL, 0, 0, 0, 0 }
2812};
2813
2814static bool parse_unit(const char* str, Res_value* outValue,
2815 float* outScale, const char** outEnd)
2816{
2817 const char* end = str;
2818 while (*end != 0 && !isspace((unsigned char)*end)) {
2819 end++;
2820 }
2821 const size_t len = end-str;
2822
2823 const char* realEnd = end;
2824 while (*realEnd != 0 && isspace((unsigned char)*realEnd)) {
2825 realEnd++;
2826 }
2827 if (*realEnd != 0) {
2828 return false;
2829 }
2830
2831 const unit_entry* cur = unitNames;
2832 while (cur->name) {
2833 if (len == cur->len && strncmp(cur->name, str, len) == 0) {
2834 outValue->dataType = cur->type;
2835 outValue->data = cur->unit << Res_value::COMPLEX_UNIT_SHIFT;
2836 *outScale = cur->scale;
2837 *outEnd = end;
2838 //printf("Found unit %s for %s\n", cur->name, str);
2839 return true;
2840 }
2841 cur++;
2842 }
2843
2844 return false;
2845}
2846
2847
2848bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue)
2849{
2850 while (len > 0 && isspace16(*s)) {
2851 s++;
2852 len--;
2853 }
2854
2855 if (len <= 0) {
2856 return false;
2857 }
2858
2859 size_t i = 0;
2860 int32_t val = 0;
2861 bool neg = false;
2862
2863 if (*s == '-') {
2864 neg = true;
2865 i++;
2866 }
2867
2868 if (s[i] < '0' || s[i] > '9') {
2869 return false;
2870 }
2871
2872 // Decimal or hex?
2873 if (s[i] == '0' && s[i+1] == 'x') {
2874 if (outValue)
2875 outValue->dataType = outValue->TYPE_INT_HEX;
2876 i += 2;
2877 bool error = false;
2878 while (i < len && !error) {
2879 val = (val*16) + get_hex(s[i], &error);
2880 i++;
2881 }
2882 if (error) {
2883 return false;
2884 }
2885 } else {
2886 if (outValue)
2887 outValue->dataType = outValue->TYPE_INT_DEC;
2888 while (i < len) {
2889 if (s[i] < '0' || s[i] > '9') {
2890 return false;
2891 }
2892 val = (val*10) + s[i]-'0';
2893 i++;
2894 }
2895 }
2896
2897 if (neg) val = -val;
2898
2899 while (i < len && isspace16(s[i])) {
2900 i++;
2901 }
2902
2903 if (i == len) {
2904 if (outValue)
2905 outValue->data = val;
2906 return true;
2907 }
2908
2909 return false;
2910}
2911
2912bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue)
2913{
2914 while (len > 0 && isspace16(*s)) {
2915 s++;
2916 len--;
2917 }
2918
2919 if (len <= 0) {
2920 return false;
2921 }
2922
2923 char buf[128];
2924 int i=0;
2925 while (len > 0 && *s != 0 && i < 126) {
2926 if (*s > 255) {
2927 return false;
2928 }
2929 buf[i++] = *s++;
2930 len--;
2931 }
2932
2933 if (len > 0) {
2934 return false;
2935 }
2936 if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') {
2937 return false;
2938 }
2939
2940 buf[i] = 0;
2941 const char* end;
2942 float f = strtof(buf, (char**)&end);
2943
2944 if (*end != 0 && !isspace((unsigned char)*end)) {
2945 // Might be a unit...
2946 float scale;
2947 if (parse_unit(end, outValue, &scale, &end)) {
2948 f *= scale;
2949 const bool neg = f < 0;
2950 if (neg) f = -f;
2951 uint64_t bits = (uint64_t)(f*(1<<23)+.5f);
2952 uint32_t radix;
2953 uint32_t shift;
2954 if ((bits&0x7fffff) == 0) {
2955 // Always use 23p0 if there is no fraction, just to make
2956 // things easier to read.
2957 radix = Res_value::COMPLEX_RADIX_23p0;
2958 shift = 23;
2959 } else if ((bits&0xffffffffff800000LL) == 0) {
2960 // Magnitude is zero -- can fit in 0 bits of precision.
2961 radix = Res_value::COMPLEX_RADIX_0p23;
2962 shift = 0;
2963 } else if ((bits&0xffffffff80000000LL) == 0) {
2964 // Magnitude can fit in 8 bits of precision.
2965 radix = Res_value::COMPLEX_RADIX_8p15;
2966 shift = 8;
2967 } else if ((bits&0xffffff8000000000LL) == 0) {
2968 // Magnitude can fit in 16 bits of precision.
2969 radix = Res_value::COMPLEX_RADIX_16p7;
2970 shift = 16;
2971 } else {
2972 // Magnitude needs entire range, so no fractional part.
2973 radix = Res_value::COMPLEX_RADIX_23p0;
2974 shift = 23;
2975 }
2976 int32_t mantissa = (int32_t)(
2977 (bits>>shift) & Res_value::COMPLEX_MANTISSA_MASK);
2978 if (neg) {
2979 mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK;
2980 }
2981 outValue->data |=
2982 (radix<<Res_value::COMPLEX_RADIX_SHIFT)
2983 | (mantissa<<Res_value::COMPLEX_MANTISSA_SHIFT);
2984 //printf("Input value: %f 0x%016Lx, mult: %f, radix: %d, shift: %d, final: 0x%08x\n",
2985 // f * (neg ? -1 : 1), bits, f*(1<<23),
2986 // radix, shift, outValue->data);
2987 return true;
2988 }
2989 return false;
2990 }
2991
2992 while (*end != 0 && isspace((unsigned char)*end)) {
2993 end++;
2994 }
2995
2996 if (*end == 0) {
2997 if (outValue) {
2998 outValue->dataType = outValue->TYPE_FLOAT;
2999 *(float*)(&outValue->data) = f;
3000 return true;
3001 }
3002 }
3003
3004 return false;
3005}
3006
3007bool ResTable::stringToValue(Res_value* outValue, String16* outString,
3008 const char16_t* s, size_t len,
3009 bool preserveSpaces, bool coerceType,
3010 uint32_t attrID,
3011 const String16* defType,
3012 const String16* defPackage,
3013 Accessor* accessor,
3014 void* accessorCookie,
3015 uint32_t attrType,
3016 bool enforcePrivate) const
3017{
3018 bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting();
3019 const char* errorMsg = NULL;
3020
3021 outValue->size = sizeof(Res_value);
3022 outValue->res0 = 0;
3023
3024 // First strip leading/trailing whitespace. Do this before handling
3025 // escapes, so they can be used to force whitespace into the string.
3026 if (!preserveSpaces) {
3027 while (len > 0 && isspace16(*s)) {
3028 s++;
3029 len--;
3030 }
3031 while (len > 0 && isspace16(s[len-1])) {
3032 len--;
3033 }
3034 // If the string ends with '\', then we keep the space after it.
3035 if (len > 0 && s[len-1] == '\\' && s[len] != 0) {
3036 len++;
3037 }
3038 }
3039
3040 //printf("Value for: %s\n", String8(s, len).string());
3041
3042 uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED;
3043 uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff;
3044 bool fromAccessor = false;
3045 if (attrID != 0 && !Res_INTERNALID(attrID)) {
3046 const ssize_t p = getResourcePackageIndex(attrID);
3047 const bag_entry* bag;
3048 ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
3049 //printf("For attr 0x%08x got bag of %d\n", attrID, cnt);
3050 if (cnt >= 0) {
3051 while (cnt > 0) {
3052 //printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data);
3053 switch (bag->map.name.ident) {
3054 case ResTable_map::ATTR_TYPE:
3055 attrType = bag->map.value.data;
3056 break;
3057 case ResTable_map::ATTR_MIN:
3058 attrMin = bag->map.value.data;
3059 break;
3060 case ResTable_map::ATTR_MAX:
3061 attrMax = bag->map.value.data;
3062 break;
3063 case ResTable_map::ATTR_L10N:
3064 l10nReq = bag->map.value.data;
3065 break;
3066 }
3067 bag++;
3068 cnt--;
3069 }
3070 unlockBag(bag);
3071 } else if (accessor && accessor->getAttributeType(attrID, &attrType)) {
3072 fromAccessor = true;
3073 if (attrType == ResTable_map::TYPE_ENUM
3074 || attrType == ResTable_map::TYPE_FLAGS
3075 || attrType == ResTable_map::TYPE_INTEGER) {
3076 accessor->getAttributeMin(attrID, &attrMin);
3077 accessor->getAttributeMax(attrID, &attrMax);
3078 }
3079 if (localizationSetting) {
3080 l10nReq = accessor->getAttributeL10N(attrID);
3081 }
3082 }
3083 }
3084
3085 const bool canStringCoerce =
3086 coerceType && (attrType&ResTable_map::TYPE_STRING) != 0;
3087
3088 if (*s == '@') {
3089 outValue->dataType = outValue->TYPE_REFERENCE;
3090
3091 // Note: we don't check attrType here because the reference can
3092 // be to any other type; we just need to count on the client making
3093 // sure the referenced type is correct.
3094
3095 //printf("Looking up ref: %s\n", String8(s, len).string());
3096
3097 // It's a reference!
3098 if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') {
3099 outValue->data = 0;
3100 return true;
3101 } else {
3102 bool createIfNotFound = false;
3103 const char16_t* resourceRefName;
3104 int resourceNameLen;
3105 if (len > 2 && s[1] == '+') {
3106 createIfNotFound = true;
3107 resourceRefName = s + 2;
3108 resourceNameLen = len - 2;
3109 } else if (len > 2 && s[1] == '*') {
3110 enforcePrivate = false;
3111 resourceRefName = s + 2;
3112 resourceNameLen = len - 2;
3113 } else {
3114 createIfNotFound = false;
3115 resourceRefName = s + 1;
3116 resourceNameLen = len - 1;
3117 }
3118 String16 package, type, name;
3119 if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name,
3120 defType, defPackage, &errorMsg)) {
3121 if (accessor != NULL) {
3122 accessor->reportError(accessorCookie, errorMsg);
3123 }
3124 return false;
3125 }
3126
3127 uint32_t specFlags = 0;
3128 uint32_t rid = identifierForName(name.string(), name.size(), type.string(),
3129 type.size(), package.string(), package.size(), &specFlags);
3130 if (rid != 0) {
3131 if (enforcePrivate) {
3132 if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
3133 if (accessor != NULL) {
3134 accessor->reportError(accessorCookie, "Resource is not public.");
3135 }
3136 return false;
3137 }
3138 }
3139 if (!accessor) {
3140 outValue->data = rid;
3141 return true;
3142 }
3143 rid = Res_MAKEID(
3144 accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
3145 Res_GETTYPE(rid), Res_GETENTRY(rid));
3146 TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n",
3147 String8(package).string(), String8(type).string(),
3148 String8(name).string(), rid));
3149 outValue->data = rid;
3150 return true;
3151 }
3152
3153 if (accessor) {
3154 uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name,
3155 createIfNotFound);
3156 if (rid != 0) {
3157 TABLE_NOISY(printf("Pckg %s:%s/%s: 0x%08x\n",
3158 String8(package).string(), String8(type).string(),
3159 String8(name).string(), rid));
3160 outValue->data = rid;
3161 return true;
3162 }
3163 }
3164 }
3165
3166 if (accessor != NULL) {
3167 accessor->reportError(accessorCookie, "No resource found that matches the given name");
3168 }
3169 return false;
3170 }
3171
3172 // if we got to here, and localization is required and it's not a reference,
3173 // complain and bail.
3174 if (l10nReq == ResTable_map::L10N_SUGGESTED) {
3175 if (localizationSetting) {
3176 if (accessor != NULL) {
3177 accessor->reportError(accessorCookie, "This attribute must be localized.");
3178 }
3179 }
3180 }
3181
3182 if (*s == '#') {
3183 // It's a color! Convert to an integer of the form 0xaarrggbb.
3184 uint32_t color = 0;
3185 bool error = false;
3186 if (len == 4) {
3187 outValue->dataType = outValue->TYPE_INT_COLOR_RGB4;
3188 color |= 0xFF000000;
3189 color |= get_hex(s[1], &error) << 20;
3190 color |= get_hex(s[1], &error) << 16;
3191 color |= get_hex(s[2], &error) << 12;
3192 color |= get_hex(s[2], &error) << 8;
3193 color |= get_hex(s[3], &error) << 4;
3194 color |= get_hex(s[3], &error);
3195 } else if (len == 5) {
3196 outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4;
3197 color |= get_hex(s[1], &error) << 28;
3198 color |= get_hex(s[1], &error) << 24;
3199 color |= get_hex(s[2], &error) << 20;
3200 color |= get_hex(s[2], &error) << 16;
3201 color |= get_hex(s[3], &error) << 12;
3202 color |= get_hex(s[3], &error) << 8;
3203 color |= get_hex(s[4], &error) << 4;
3204 color |= get_hex(s[4], &error);
3205 } else if (len == 7) {
3206 outValue->dataType = outValue->TYPE_INT_COLOR_RGB8;
3207 color |= 0xFF000000;
3208 color |= get_hex(s[1], &error) << 20;
3209 color |= get_hex(s[2], &error) << 16;
3210 color |= get_hex(s[3], &error) << 12;
3211 color |= get_hex(s[4], &error) << 8;
3212 color |= get_hex(s[5], &error) << 4;
3213 color |= get_hex(s[6], &error);
3214 } else if (len == 9) {
3215 outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8;
3216 color |= get_hex(s[1], &error) << 28;
3217 color |= get_hex(s[2], &error) << 24;
3218 color |= get_hex(s[3], &error) << 20;
3219 color |= get_hex(s[4], &error) << 16;
3220 color |= get_hex(s[5], &error) << 12;
3221 color |= get_hex(s[6], &error) << 8;
3222 color |= get_hex(s[7], &error) << 4;
3223 color |= get_hex(s[8], &error);
3224 } else {
3225 error = true;
3226 }
3227 if (!error) {
3228 if ((attrType&ResTable_map::TYPE_COLOR) == 0) {
3229 if (!canStringCoerce) {
3230 if (accessor != NULL) {
3231 accessor->reportError(accessorCookie,
3232 "Color types not allowed");
3233 }
3234 return false;
3235 }
3236 } else {
3237 outValue->data = color;
3238 //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color);
3239 return true;
3240 }
3241 } else {
3242 if ((attrType&ResTable_map::TYPE_COLOR) != 0) {
3243 if (accessor != NULL) {
3244 accessor->reportError(accessorCookie, "Color value not valid --"
3245 " must be #rgb, #argb, #rrggbb, or #aarrggbb");
3246 }
3247 #if 0
3248 fprintf(stderr, "%s: Color ID %s value %s is not valid\n",
3249 "Resource File", //(const char*)in->getPrintableSource(),
3250 String8(*curTag).string(),
3251 String8(s, len).string());
3252 #endif
3253 return false;
3254 }
3255 }
3256 }
3257
3258 if (*s == '?') {
3259 outValue->dataType = outValue->TYPE_ATTRIBUTE;
3260
3261 // Note: we don't check attrType here because the reference can
3262 // be to any other type; we just need to count on the client making
3263 // sure the referenced type is correct.
3264
3265 //printf("Looking up attr: %s\n", String8(s, len).string());
3266
3267 static const String16 attr16("attr");
3268 String16 package, type, name;
3269 if (!expandResourceRef(s+1, len-1, &package, &type, &name,
3270 &attr16, defPackage, &errorMsg)) {
3271 if (accessor != NULL) {
3272 accessor->reportError(accessorCookie, errorMsg);
3273 }
3274 return false;
3275 }
3276
3277 //printf("Pkg: %s, Type: %s, Name: %s\n",
3278 // String8(package).string(), String8(type).string(),
3279 // String8(name).string());
3280 uint32_t specFlags = 0;
3281 uint32_t rid =
3282 identifierForName(name.string(), name.size(),
3283 type.string(), type.size(),
3284 package.string(), package.size(), &specFlags);
3285 if (rid != 0) {
3286 if (enforcePrivate) {
3287 if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
3288 if (accessor != NULL) {
3289 accessor->reportError(accessorCookie, "Attribute is not public.");
3290 }
3291 return false;
3292 }
3293 }
3294 if (!accessor) {
3295 outValue->data = rid;
3296 return true;
3297 }
3298 rid = Res_MAKEID(
3299 accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
3300 Res_GETTYPE(rid), Res_GETENTRY(rid));
3301 //printf("Incl %s:%s/%s: 0x%08x\n",
3302 // String8(package).string(), String8(type).string(),
3303 // String8(name).string(), rid);
3304 outValue->data = rid;
3305 return true;
3306 }
3307
3308 if (accessor) {
3309 uint32_t rid = accessor->getCustomResource(package, type, name);
3310 if (rid != 0) {
3311 //printf("Mine %s:%s/%s: 0x%08x\n",
3312 // String8(package).string(), String8(type).string(),
3313 // String8(name).string(), rid);
3314 outValue->data = rid;
3315 return true;
3316 }
3317 }
3318
3319 if (accessor != NULL) {
3320 accessor->reportError(accessorCookie, "No resource found that matches the given name");
3321 }
3322 return false;
3323 }
3324
3325 if (stringToInt(s, len, outValue)) {
3326 if ((attrType&ResTable_map::TYPE_INTEGER) == 0) {
3327 // If this type does not allow integers, but does allow floats,
3328 // fall through on this error case because the float type should
3329 // be able to accept any integer value.
3330 if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) {
3331 if (accessor != NULL) {
3332 accessor->reportError(accessorCookie, "Integer types not allowed");
3333 }
3334 return false;
3335 }
3336 } else {
3337 if (((int32_t)outValue->data) < ((int32_t)attrMin)
3338 || ((int32_t)outValue->data) > ((int32_t)attrMax)) {
3339 if (accessor != NULL) {
3340 accessor->reportError(accessorCookie, "Integer value out of range");
3341 }
3342 return false;
3343 }
3344 return true;
3345 }
3346 }
3347
3348 if (stringToFloat(s, len, outValue)) {
3349 if (outValue->dataType == Res_value::TYPE_DIMENSION) {
3350 if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) {
3351 return true;
3352 }
3353 if (!canStringCoerce) {
3354 if (accessor != NULL) {
3355 accessor->reportError(accessorCookie, "Dimension types not allowed");
3356 }
3357 return false;
3358 }
3359 } else if (outValue->dataType == Res_value::TYPE_FRACTION) {
3360 if ((attrType&ResTable_map::TYPE_FRACTION) != 0) {
3361 return true;
3362 }
3363 if (!canStringCoerce) {
3364 if (accessor != NULL) {
3365 accessor->reportError(accessorCookie, "Fraction types not allowed");
3366 }
3367 return false;
3368 }
3369 } else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) {
3370 if (!canStringCoerce) {
3371 if (accessor != NULL) {
3372 accessor->reportError(accessorCookie, "Float types not allowed");
3373 }
3374 return false;
3375 }
3376 } else {
3377 return true;
3378 }
3379 }
3380
3381 if (len == 4) {
3382 if ((s[0] == 't' || s[0] == 'T') &&
3383 (s[1] == 'r' || s[1] == 'R') &&
3384 (s[2] == 'u' || s[2] == 'U') &&
3385 (s[3] == 'e' || s[3] == 'E')) {
3386 if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) {
3387 if (!canStringCoerce) {
3388 if (accessor != NULL) {
3389 accessor->reportError(accessorCookie, "Boolean types not allowed");
3390 }
3391 return false;
3392 }
3393 } else {
3394 outValue->dataType = outValue->TYPE_INT_BOOLEAN;
3395 outValue->data = (uint32_t)-1;
3396 return true;
3397 }
3398 }
3399 }
3400
3401 if (len == 5) {
3402 if ((s[0] == 'f' || s[0] == 'F') &&
3403 (s[1] == 'a' || s[1] == 'A') &&
3404 (s[2] == 'l' || s[2] == 'L') &&
3405 (s[3] == 's' || s[3] == 'S') &&
3406 (s[4] == 'e' || s[4] == 'E')) {
3407 if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) {
3408 if (!canStringCoerce) {
3409 if (accessor != NULL) {
3410 accessor->reportError(accessorCookie, "Boolean types not allowed");
3411 }
3412 return false;
3413 }
3414 } else {
3415 outValue->dataType = outValue->TYPE_INT_BOOLEAN;
3416 outValue->data = 0;
3417 return true;
3418 }
3419 }
3420 }
3421
3422 if ((attrType&ResTable_map::TYPE_ENUM) != 0) {
3423 const ssize_t p = getResourcePackageIndex(attrID);
3424 const bag_entry* bag;
3425 ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
3426 //printf("Got %d for enum\n", cnt);
3427 if (cnt >= 0) {
3428 resource_name rname;
3429 while (cnt > 0) {
3430 if (!Res_INTERNALID(bag->map.name.ident)) {
3431 //printf("Trying attr #%08x\n", bag->map.name.ident);
3432 if (getResourceName(bag->map.name.ident, &rname)) {
3433 #if 0
3434 printf("Matching %s against %s (0x%08x)\n",
3435 String8(s, len).string(),
3436 String8(rname.name, rname.nameLen).string(),
3437 bag->map.name.ident);
3438 #endif
3439 if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) {
3440 outValue->dataType = bag->map.value.dataType;
3441 outValue->data = bag->map.value.data;
3442 unlockBag(bag);
3443 return true;
3444 }
3445 }
3446
3447 }
3448 bag++;
3449 cnt--;
3450 }
3451 unlockBag(bag);
3452 }
3453
3454 if (fromAccessor) {
3455 if (accessor->getAttributeEnum(attrID, s, len, outValue)) {
3456 return true;
3457 }
3458 }
3459 }
3460
3461 if ((attrType&ResTable_map::TYPE_FLAGS) != 0) {
3462 const ssize_t p = getResourcePackageIndex(attrID);
3463 const bag_entry* bag;
3464 ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
3465 //printf("Got %d for flags\n", cnt);
3466 if (cnt >= 0) {
3467 bool failed = false;
3468 resource_name rname;
3469 outValue->dataType = Res_value::TYPE_INT_HEX;
3470 outValue->data = 0;
3471 const char16_t* end = s + len;
3472 const char16_t* pos = s;
3473 while (pos < end && !failed) {
3474 const char16_t* start = pos;
The Android Open Source Project4df24232009-03-05 14:34:35 -08003475 pos++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003476 while (pos < end && *pos != '|') {
3477 pos++;
3478 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08003479 //printf("Looking for: %s\n", String8(start, pos-start).string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003480 const bag_entry* bagi = bag;
The Android Open Source Project4df24232009-03-05 14:34:35 -08003481 ssize_t i;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003482 for (i=0; i<cnt; i++, bagi++) {
3483 if (!Res_INTERNALID(bagi->map.name.ident)) {
3484 //printf("Trying attr #%08x\n", bagi->map.name.ident);
3485 if (getResourceName(bagi->map.name.ident, &rname)) {
3486 #if 0
3487 printf("Matching %s against %s (0x%08x)\n",
3488 String8(start,pos-start).string(),
3489 String8(rname.name, rname.nameLen).string(),
3490 bagi->map.name.ident);
3491 #endif
3492 if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) {
3493 outValue->data |= bagi->map.value.data;
3494 break;
3495 }
3496 }
3497 }
3498 }
3499 if (i >= cnt) {
3500 // Didn't find this flag identifier.
3501 failed = true;
3502 }
3503 if (pos < end) {
3504 pos++;
3505 }
3506 }
3507 unlockBag(bag);
3508 if (!failed) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08003509 //printf("Final flag value: 0x%lx\n", outValue->data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003510 return true;
3511 }
3512 }
3513
3514
3515 if (fromAccessor) {
3516 if (accessor->getAttributeFlags(attrID, s, len, outValue)) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08003517 //printf("Final flag value: 0x%lx\n", outValue->data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003518 return true;
3519 }
3520 }
3521 }
3522
3523 if ((attrType&ResTable_map::TYPE_STRING) == 0) {
3524 if (accessor != NULL) {
3525 accessor->reportError(accessorCookie, "String types not allowed");
3526 }
3527 return false;
3528 }
3529
3530 // Generic string handling...
3531 outValue->dataType = outValue->TYPE_STRING;
3532 if (outString) {
3533 bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg);
3534 if (accessor != NULL) {
3535 accessor->reportError(accessorCookie, errorMsg);
3536 }
3537 return failed;
3538 }
3539
3540 return true;
3541}
3542
3543bool ResTable::collectString(String16* outString,
3544 const char16_t* s, size_t len,
3545 bool preserveSpaces,
3546 const char** outErrorMsg,
3547 bool append)
3548{
3549 String16 tmp;
3550
3551 char quoted = 0;
3552 const char16_t* p = s;
3553 while (p < (s+len)) {
3554 while (p < (s+len)) {
3555 const char16_t c = *p;
3556 if (c == '\\') {
3557 break;
3558 }
3559 if (!preserveSpaces) {
3560 if (quoted == 0 && isspace16(c)
3561 && (c != ' ' || isspace16(*(p+1)))) {
3562 break;
3563 }
3564 if (c == '"' && (quoted == 0 || quoted == '"')) {
3565 break;
3566 }
3567 if (c == '\'' && (quoted == 0 || quoted == '\'')) {
Eric Fischerc87d2522009-09-01 15:20:30 -07003568 /*
3569 * In practice, when people write ' instead of \'
3570 * in a string, they are doing it by accident
3571 * instead of really meaning to use ' as a quoting
3572 * character. Warn them so they don't lose it.
3573 */
3574 if (outErrorMsg) {
3575 *outErrorMsg = "Apostrophe not preceded by \\";
3576 }
3577 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003578 }
3579 }
3580 p++;
3581 }
3582 if (p < (s+len)) {
3583 if (p > s) {
3584 tmp.append(String16(s, p-s));
3585 }
3586 if (!preserveSpaces && (*p == '"' || *p == '\'')) {
3587 if (quoted == 0) {
3588 quoted = *p;
3589 } else {
3590 quoted = 0;
3591 }
3592 p++;
3593 } else if (!preserveSpaces && isspace16(*p)) {
3594 // Space outside of a quote -- consume all spaces and
3595 // leave a single plain space char.
3596 tmp.append(String16(" "));
3597 p++;
3598 while (p < (s+len) && isspace16(*p)) {
3599 p++;
3600 }
3601 } else if (*p == '\\') {
3602 p++;
3603 if (p < (s+len)) {
3604 switch (*p) {
3605 case 't':
3606 tmp.append(String16("\t"));
3607 break;
3608 case 'n':
3609 tmp.append(String16("\n"));
3610 break;
3611 case '#':
3612 tmp.append(String16("#"));
3613 break;
3614 case '@':
3615 tmp.append(String16("@"));
3616 break;
3617 case '?':
3618 tmp.append(String16("?"));
3619 break;
3620 case '"':
3621 tmp.append(String16("\""));
3622 break;
3623 case '\'':
3624 tmp.append(String16("'"));
3625 break;
3626 case '\\':
3627 tmp.append(String16("\\"));
3628 break;
3629 case 'u':
3630 {
3631 char16_t chr = 0;
3632 int i = 0;
3633 while (i < 4 && p[1] != 0) {
3634 p++;
3635 i++;
3636 int c;
3637 if (*p >= '0' && *p <= '9') {
3638 c = *p - '0';
3639 } else if (*p >= 'a' && *p <= 'f') {
3640 c = *p - 'a' + 10;
3641 } else if (*p >= 'A' && *p <= 'F') {
3642 c = *p - 'A' + 10;
3643 } else {
3644 if (outErrorMsg) {
3645 *outErrorMsg = "Bad character in \\u unicode escape sequence";
3646 }
3647 return false;
3648 }
3649 chr = (chr<<4) | c;
3650 }
3651 tmp.append(String16(&chr, 1));
3652 } break;
3653 default:
3654 // ignore unknown escape chars.
3655 break;
3656 }
3657 p++;
3658 }
3659 }
3660 len -= (p-s);
3661 s = p;
3662 }
3663 }
3664
3665 if (tmp.size() != 0) {
3666 if (len > 0) {
3667 tmp.append(String16(s, len));
3668 }
3669 if (append) {
3670 outString->append(tmp);
3671 } else {
3672 outString->setTo(tmp);
3673 }
3674 } else {
3675 if (append) {
3676 outString->append(String16(s, len));
3677 } else {
3678 outString->setTo(s, len);
3679 }
3680 }
3681
3682 return true;
3683}
3684
3685size_t ResTable::getBasePackageCount() const
3686{
3687 if (mError != NO_ERROR) {
3688 return 0;
3689 }
3690 return mPackageGroups.size();
3691}
3692
3693const char16_t* ResTable::getBasePackageName(size_t idx) const
3694{
3695 if (mError != NO_ERROR) {
3696 return 0;
3697 }
3698 LOG_FATAL_IF(idx >= mPackageGroups.size(),
3699 "Requested package index %d past package count %d",
3700 (int)idx, (int)mPackageGroups.size());
3701 return mPackageGroups[idx]->name.string();
3702}
3703
3704uint32_t ResTable::getBasePackageId(size_t idx) const
3705{
3706 if (mError != NO_ERROR) {
3707 return 0;
3708 }
3709 LOG_FATAL_IF(idx >= mPackageGroups.size(),
3710 "Requested package index %d past package count %d",
3711 (int)idx, (int)mPackageGroups.size());
3712 return mPackageGroups[idx]->id;
3713}
3714
3715size_t ResTable::getTableCount() const
3716{
3717 return mHeaders.size();
3718}
3719
3720const ResStringPool* ResTable::getTableStringBlock(size_t index) const
3721{
3722 return &mHeaders[index]->values;
3723}
3724
3725void* ResTable::getTableCookie(size_t index) const
3726{
3727 return mHeaders[index]->cookie;
3728}
3729
3730void ResTable::getConfigurations(Vector<ResTable_config>* configs) const
3731{
3732 const size_t I = mPackageGroups.size();
3733 for (size_t i=0; i<I; i++) {
3734 const PackageGroup* packageGroup = mPackageGroups[i];
3735 const size_t J = packageGroup->packages.size();
3736 for (size_t j=0; j<J; j++) {
3737 const Package* package = packageGroup->packages[j];
3738 const size_t K = package->types.size();
3739 for (size_t k=0; k<K; k++) {
3740 const Type* type = package->types[k];
3741 if (type == NULL) continue;
3742 const size_t L = type->configs.size();
3743 for (size_t l=0; l<L; l++) {
3744 const ResTable_type* config = type->configs[l];
3745 const ResTable_config* cfg = &config->config;
3746 // only insert unique
3747 const size_t M = configs->size();
3748 size_t m;
3749 for (m=0; m<M; m++) {
3750 if (0 == (*configs)[m].compare(*cfg)) {
3751 break;
3752 }
3753 }
3754 // if we didn't find it
3755 if (m == M) {
3756 configs->add(*cfg);
3757 }
3758 }
3759 }
3760 }
3761 }
3762}
3763
3764void ResTable::getLocales(Vector<String8>* locales) const
3765{
3766 Vector<ResTable_config> configs;
3767 LOGD("calling getConfigurations");
3768 getConfigurations(&configs);
3769 LOGD("called getConfigurations size=%d", (int)configs.size());
3770 const size_t I = configs.size();
3771 for (size_t i=0; i<I; i++) {
3772 char locale[6];
3773 configs[i].getLocale(locale);
3774 const size_t J = locales->size();
3775 size_t j;
3776 for (j=0; j<J; j++) {
3777 if (0 == strcmp(locale, (*locales)[j].string())) {
3778 break;
3779 }
3780 }
3781 if (j == J) {
3782 locales->add(String8(locale));
3783 }
3784 }
3785}
3786
3787ssize_t ResTable::getEntry(
3788 const Package* package, int typeIndex, int entryIndex,
3789 const ResTable_config* config,
3790 const ResTable_type** outType, const ResTable_entry** outEntry,
3791 const Type** outTypeClass) const
3792{
3793 LOGV("Getting entry from package %p\n", package);
3794 const ResTable_package* const pkg = package->package;
3795
3796 const Type* allTypes = package->getType(typeIndex);
3797 LOGV("allTypes=%p\n", allTypes);
3798 if (allTypes == NULL) {
3799 LOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
3800 return 0;
3801 }
3802
3803 if ((size_t)entryIndex >= allTypes->entryCount) {
3804 LOGW("getEntry failing because entryIndex %d is beyond type entryCount %d",
3805 entryIndex, (int)allTypes->entryCount);
3806 return BAD_TYPE;
3807 }
3808
3809 const ResTable_type* type = NULL;
3810 uint32_t offset = ResTable_type::NO_ENTRY;
3811 ResTable_config bestConfig;
3812 memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up
3813
3814 const size_t NT = allTypes->configs.size();
3815 for (size_t i=0; i<NT; i++) {
3816 const ResTable_type* const thisType = allTypes->configs[i];
3817 if (thisType == NULL) continue;
3818
3819 ResTable_config thisConfig;
3820 thisConfig.copyFromDtoH(thisType->config);
3821
3822 TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): imsi:%d/%d=%d/%d lang:%c%c=%c%c cnt:%c%c=%c%c "
3823 "orien:%d=%d touch:%d=%d density:%d=%d key:%d=%d inp:%d=%d nav:%d=%d w:%d=%d h:%d=%d\n",
3824 entryIndex, typeIndex+1, dtohl(thisType->config.size),
3825 thisConfig.mcc, thisConfig.mnc,
3826 config ? config->mcc : 0, config ? config->mnc : 0,
3827 thisConfig.language[0] ? thisConfig.language[0] : '-',
3828 thisConfig.language[1] ? thisConfig.language[1] : '-',
3829 config && config->language[0] ? config->language[0] : '-',
3830 config && config->language[1] ? config->language[1] : '-',
3831 thisConfig.country[0] ? thisConfig.country[0] : '-',
3832 thisConfig.country[1] ? thisConfig.country[1] : '-',
3833 config && config->country[0] ? config->country[0] : '-',
3834 config && config->country[1] ? config->country[1] : '-',
3835 thisConfig.orientation,
3836 config ? config->orientation : 0,
3837 thisConfig.touchscreen,
3838 config ? config->touchscreen : 0,
3839 thisConfig.density,
3840 config ? config->density : 0,
3841 thisConfig.keyboard,
3842 config ? config->keyboard : 0,
3843 thisConfig.inputFlags,
3844 config ? config->inputFlags : 0,
3845 thisConfig.navigation,
3846 config ? config->navigation : 0,
3847 thisConfig.screenWidth,
3848 config ? config->screenWidth : 0,
3849 thisConfig.screenHeight,
3850 config ? config->screenHeight : 0));
3851
3852 // Check to make sure this one is valid for the current parameters.
3853 if (config && !thisConfig.match(*config)) {
3854 TABLE_GETENTRY(LOGI("Does not match config!\n"));
3855 continue;
3856 }
3857
3858 // Check if there is the desired entry in this type.
3859
3860 const uint8_t* const end = ((const uint8_t*)thisType)
3861 + dtohl(thisType->header.size);
3862 const uint32_t* const eindex = (const uint32_t*)
3863 (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize));
3864
3865 uint32_t thisOffset = dtohl(eindex[entryIndex]);
3866 if (thisOffset == ResTable_type::NO_ENTRY) {
3867 TABLE_GETENTRY(LOGI("Skipping because it is not defined!\n"));
3868 continue;
3869 }
3870
3871 if (type != NULL) {
3872 // Check if this one is less specific than the last found. If so,
3873 // we will skip it. We check starting with things we most care
3874 // about to those we least care about.
3875 if (!thisConfig.isBetterThan(bestConfig, config)) {
3876 TABLE_GETENTRY(LOGI("This config is worse than last!\n"));
3877 continue;
3878 }
3879 }
3880
3881 type = thisType;
3882 offset = thisOffset;
3883 bestConfig = thisConfig;
3884 TABLE_GETENTRY(LOGI("Best entry so far -- using it!\n"));
3885 if (!config) break;
3886 }
3887
3888 if (type == NULL) {
3889 TABLE_GETENTRY(LOGI("No value found for requested entry!\n"));
3890 return BAD_INDEX;
3891 }
3892
3893 offset += dtohl(type->entriesStart);
3894 TABLE_NOISY(aout << "Looking in resource table " << package->header->header
3895 << ", typeOff="
3896 << (void*)(((const char*)type)-((const char*)package->header->header))
3897 << ", offset=" << (void*)offset << endl);
3898
3899 if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) {
3900 LOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x",
3901 offset, dtohl(type->header.size));
3902 return BAD_TYPE;
3903 }
3904 if ((offset&0x3) != 0) {
3905 LOGW("ResTable_entry at 0x%x is not on an integer boundary",
3906 offset);
3907 return BAD_TYPE;
3908 }
3909
3910 const ResTable_entry* const entry = (const ResTable_entry*)
3911 (((const uint8_t*)type) + offset);
3912 if (dtohs(entry->size) < sizeof(*entry)) {
3913 LOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size));
3914 return BAD_TYPE;
3915 }
3916
3917 *outType = type;
3918 *outEntry = entry;
3919 if (outTypeClass != NULL) {
3920 *outTypeClass = allTypes;
3921 }
3922 return offset + dtohs(entry->size);
3923}
3924
3925status_t ResTable::parsePackage(const ResTable_package* const pkg,
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01003926 const Header* const header, uint32_t idmap_id)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003927{
3928 const uint8_t* base = (const uint8_t*)pkg;
3929 status_t err = validate_chunk(&pkg->header, sizeof(*pkg),
3930 header->dataEnd, "ResTable_package");
3931 if (err != NO_ERROR) {
3932 return (mError=err);
3933 }
3934
3935 const size_t pkgSize = dtohl(pkg->header.size);
3936
3937 if (dtohl(pkg->typeStrings) >= pkgSize) {
3938 LOGW("ResTable_package type strings at %p are past chunk size %p.",
3939 (void*)dtohl(pkg->typeStrings), (void*)pkgSize);
3940 return (mError=BAD_TYPE);
3941 }
3942 if ((dtohl(pkg->typeStrings)&0x3) != 0) {
3943 LOGW("ResTable_package type strings at %p is not on an integer boundary.",
3944 (void*)dtohl(pkg->typeStrings));
3945 return (mError=BAD_TYPE);
3946 }
3947 if (dtohl(pkg->keyStrings) >= pkgSize) {
3948 LOGW("ResTable_package key strings at %p are past chunk size %p.",
3949 (void*)dtohl(pkg->keyStrings), (void*)pkgSize);
3950 return (mError=BAD_TYPE);
3951 }
3952 if ((dtohl(pkg->keyStrings)&0x3) != 0) {
3953 LOGW("ResTable_package key strings at %p is not on an integer boundary.",
3954 (void*)dtohl(pkg->keyStrings));
3955 return (mError=BAD_TYPE);
3956 }
3957
3958 Package* package = NULL;
3959 PackageGroup* group = NULL;
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01003960 uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id);
3961 // If at this point id == 0, pkg is an overlay package without a
3962 // corresponding idmap. During regular usage, overlay packages are
3963 // always loaded alongside their idmaps, but during idmap creation
3964 // the package is temporarily loaded by itself.
3965 if (id < 256) {
Dianne Hackborn78c40512009-07-06 11:07:40 -07003966
3967 package = new Package(this, header, pkg);
3968 if (package == NULL) {
3969 return (mError=NO_MEMORY);
3970 }
3971
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003972 size_t idx = mPackageMap[id];
3973 if (idx == 0) {
3974 idx = mPackageGroups.size()+1;
3975
3976 char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)];
3977 strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t));
Dianne Hackborn78c40512009-07-06 11:07:40 -07003978 group = new PackageGroup(this, String16(tmpName), id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003979 if (group == NULL) {
Dianne Hackborn78c40512009-07-06 11:07:40 -07003980 delete package;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003981 return (mError=NO_MEMORY);
3982 }
3983
Dianne Hackborn78c40512009-07-06 11:07:40 -07003984 err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003985 header->dataEnd-(base+dtohl(pkg->typeStrings)));
3986 if (err != NO_ERROR) {
Dianne Hackborn78c40512009-07-06 11:07:40 -07003987 delete group;
3988 delete package;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003989 return (mError=err);
3990 }
Dianne Hackborn78c40512009-07-06 11:07:40 -07003991 err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003992 header->dataEnd-(base+dtohl(pkg->keyStrings)));
3993 if (err != NO_ERROR) {
Dianne Hackborn78c40512009-07-06 11:07:40 -07003994 delete group;
3995 delete package;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003996 return (mError=err);
3997 }
3998
3999 //printf("Adding new package id %d at index %d\n", id, idx);
4000 err = mPackageGroups.add(group);
4001 if (err < NO_ERROR) {
4002 return (mError=err);
4003 }
Dianne Hackborn78c40512009-07-06 11:07:40 -07004004 group->basePackage = package;
4005
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004006 mPackageMap[id] = (uint8_t)idx;
4007 } else {
4008 group = mPackageGroups.itemAt(idx-1);
4009 if (group == NULL) {
4010 return (mError=UNKNOWN_ERROR);
4011 }
4012 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004013 err = group->packages.add(package);
4014 if (err < NO_ERROR) {
4015 return (mError=err);
4016 }
4017 } else {
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01004018 LOG_ALWAYS_FATAL("Package id out of range");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004019 return NO_ERROR;
4020 }
4021
4022
4023 // Iterate through all chunks.
4024 size_t curPackage = 0;
4025
4026 const ResChunk_header* chunk =
4027 (const ResChunk_header*)(((const uint8_t*)pkg)
4028 + dtohs(pkg->header.headerSize));
4029 const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size);
4030 while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) &&
4031 ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) {
4032 TABLE_NOISY(LOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n",
4033 dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size),
4034 (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))));
4035 const size_t csize = dtohl(chunk->size);
4036 const uint16_t ctype = dtohs(chunk->type);
4037 if (ctype == RES_TABLE_TYPE_SPEC_TYPE) {
4038 const ResTable_typeSpec* typeSpec = (const ResTable_typeSpec*)(chunk);
4039 err = validate_chunk(&typeSpec->header, sizeof(*typeSpec),
4040 endPos, "ResTable_typeSpec");
4041 if (err != NO_ERROR) {
4042 return (mError=err);
4043 }
4044
4045 const size_t typeSpecSize = dtohl(typeSpec->header.size);
4046
4047 LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n",
4048 (void*)(base-(const uint8_t*)chunk),
4049 dtohs(typeSpec->header.type),
4050 dtohs(typeSpec->header.headerSize),
4051 (void*)typeSize));
4052 // look for block overrun or int overflow when multiplying by 4
4053 if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t))
4054 || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount))
4055 > typeSpecSize)) {
4056 LOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.",
4057 (void*)(dtohs(typeSpec->header.headerSize)
4058 +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))),
4059 (void*)typeSpecSize);
4060 return (mError=BAD_TYPE);
4061 }
4062
4063 if (typeSpec->id == 0) {
4064 LOGW("ResTable_type has an id of 0.");
4065 return (mError=BAD_TYPE);
4066 }
4067
4068 while (package->types.size() < typeSpec->id) {
4069 package->types.add(NULL);
4070 }
4071 Type* t = package->types[typeSpec->id-1];
4072 if (t == NULL) {
4073 t = new Type(header, package, dtohl(typeSpec->entryCount));
4074 package->types.editItemAt(typeSpec->id-1) = t;
4075 } else if (dtohl(typeSpec->entryCount) != t->entryCount) {
4076 LOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d",
4077 (int)dtohl(typeSpec->entryCount), (int)t->entryCount);
4078 return (mError=BAD_TYPE);
4079 }
4080 t->typeSpecFlags = (const uint32_t*)(
4081 ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
4082 t->typeSpec = typeSpec;
4083
4084 } else if (ctype == RES_TABLE_TYPE_TYPE) {
4085 const ResTable_type* type = (const ResTable_type*)(chunk);
4086 err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4,
4087 endPos, "ResTable_type");
4088 if (err != NO_ERROR) {
4089 return (mError=err);
4090 }
4091
4092 const size_t typeSize = dtohl(type->header.size);
4093
4094 LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n",
4095 (void*)(base-(const uint8_t*)chunk),
4096 dtohs(type->header.type),
4097 dtohs(type->header.headerSize),
4098 (void*)typeSize));
4099 if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount))
4100 > typeSize) {
4101 LOGW("ResTable_type entry index to %p extends beyond chunk end %p.",
4102 (void*)(dtohs(type->header.headerSize)
4103 +(sizeof(uint32_t)*dtohl(type->entryCount))),
4104 (void*)typeSize);
4105 return (mError=BAD_TYPE);
4106 }
4107 if (dtohl(type->entryCount) != 0
4108 && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) {
4109 LOGW("ResTable_type entriesStart at %p extends beyond chunk end %p.",
4110 (void*)dtohl(type->entriesStart), (void*)typeSize);
4111 return (mError=BAD_TYPE);
4112 }
4113 if (type->id == 0) {
4114 LOGW("ResTable_type has an id of 0.");
4115 return (mError=BAD_TYPE);
4116 }
4117
4118 while (package->types.size() < type->id) {
4119 package->types.add(NULL);
4120 }
4121 Type* t = package->types[type->id-1];
4122 if (t == NULL) {
4123 t = new Type(header, package, dtohl(type->entryCount));
4124 package->types.editItemAt(type->id-1) = t;
4125 } else if (dtohl(type->entryCount) != t->entryCount) {
4126 LOGW("ResTable_type entry count inconsistent: given %d, previously %d",
4127 (int)dtohl(type->entryCount), (int)t->entryCount);
4128 return (mError=BAD_TYPE);
4129 }
4130
4131 TABLE_GETENTRY(
4132 ResTable_config thisConfig;
4133 thisConfig.copyFromDtoH(type->config);
4134 LOGI("Adding config to type %d: imsi:%d/%d lang:%c%c cnt:%c%c "
4135 "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
4136 type->id,
4137 thisConfig.mcc, thisConfig.mnc,
4138 thisConfig.language[0] ? thisConfig.language[0] : '-',
4139 thisConfig.language[1] ? thisConfig.language[1] : '-',
4140 thisConfig.country[0] ? thisConfig.country[0] : '-',
4141 thisConfig.country[1] ? thisConfig.country[1] : '-',
4142 thisConfig.orientation,
4143 thisConfig.touchscreen,
4144 thisConfig.density,
4145 thisConfig.keyboard,
4146 thisConfig.inputFlags,
4147 thisConfig.navigation,
4148 thisConfig.screenWidth,
4149 thisConfig.screenHeight));
4150 t->configs.add(type);
4151 } else {
4152 status_t err = validate_chunk(chunk, sizeof(ResChunk_header),
4153 endPos, "ResTable_package:unknown");
4154 if (err != NO_ERROR) {
4155 return (mError=err);
4156 }
4157 }
4158 chunk = (const ResChunk_header*)
4159 (((const uint8_t*)chunk) + csize);
4160 }
4161
4162 if (group->typeCount == 0) {
4163 group->typeCount = package->types.size();
4164 }
4165
4166 return NO_ERROR;
4167}
4168
MÃ¥rten Kongstad57f4b772011-03-17 14:13:41 +01004169status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, uint32_t overlayCrc,
4170 void** outData, size_t* outSize) const
4171{
4172 // see README for details on the format of map
4173 if (mPackageGroups.size() == 0) {
4174 return UNKNOWN_ERROR;
4175 }
4176 if (mPackageGroups[0]->packages.size() == 0) {
4177 return UNKNOWN_ERROR;
4178 }
4179
4180 Vector<Vector<uint32_t> > map;
4181 const PackageGroup* pg = mPackageGroups[0];
4182 const Package* pkg = pg->packages[0];
4183 size_t typeCount = pkg->types.size();
4184 // starting size is header + first item (number of types in map)
4185 *outSize = (IDMAP_HEADER_SIZE + 1) * sizeof(uint32_t);
4186 const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name);
4187 const uint32_t pkg_id = pkg->package->id << 24;
4188
4189 for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) {
4190 ssize_t offset = -1;
4191 const Type* typeConfigs = pkg->getType(typeIndex);
4192 ssize_t mapIndex = map.add();
4193 if (mapIndex < 0) {
4194 return NO_MEMORY;
4195 }
4196 Vector<uint32_t>& vector = map.editItemAt(mapIndex);
4197 for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
4198 uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
4199 | (0x00ff0000 & ((typeIndex+1)<<16))
4200 | (0x0000ffff & (entryIndex));
4201 resource_name resName;
4202 if (!this->getResourceName(resID, &resName)) {
4203 return UNKNOWN_ERROR;
4204 }
4205
4206 const String16 overlayType(resName.type, resName.typeLen);
4207 const String16 overlayName(resName.name, resName.nameLen);
4208 uint32_t overlayResID = overlay.identifierForName(overlayName.string(),
4209 overlayName.size(),
4210 overlayType.string(),
4211 overlayType.size(),
4212 overlayPackage.string(),
4213 overlayPackage.size());
4214 if (overlayResID != 0) {
4215 // overlay package has package ID == 0, use original package's ID instead
4216 overlayResID |= pkg_id;
4217 }
4218 vector.push(overlayResID);
4219 if (overlayResID != 0 && offset == -1) {
4220 offset = Res_GETENTRY(resID);
4221 }
4222#if 0
4223 if (overlayResID != 0) {
4224 LOGD("%s/%s 0x%08x -> 0x%08x\n",
4225 String8(String16(resName.type)).string(),
4226 String8(String16(resName.name)).string(),
4227 resID, overlayResID);
4228 }
4229#endif
4230 }
4231
4232 if (offset != -1) {
4233 // shave off leading and trailing entries which lack overlay values
4234 vector.removeItemsAt(0, offset);
4235 vector.insertAt((uint32_t)offset, 0, 1);
4236 while (vector.top() == 0) {
4237 vector.pop();
4238 }
4239 // reserve space for number and offset of entries, and the actual entries
4240 *outSize += (2 + vector.size()) * sizeof(uint32_t);
4241 } else {
4242 // no entries of current type defined in overlay package
4243 vector.clear();
4244 // reserve space for type offset
4245 *outSize += 1 * sizeof(uint32_t);
4246 }
4247 }
4248
4249 if ((*outData = malloc(*outSize)) == NULL) {
4250 return NO_MEMORY;
4251 }
4252 uint32_t* data = (uint32_t*)*outData;
4253 *data++ = htodl(IDMAP_MAGIC);
4254 *data++ = htodl(originalCrc);
4255 *data++ = htodl(overlayCrc);
4256 const size_t mapSize = map.size();
4257 *data++ = htodl(mapSize);
4258 size_t offset = mapSize;
4259 for (size_t i = 0; i < mapSize; ++i) {
4260 const Vector<uint32_t>& vector = map.itemAt(i);
4261 const size_t N = vector.size();
4262 if (N == 0) {
4263 *data++ = htodl(0);
4264 } else {
4265 offset++;
4266 *data++ = htodl(offset);
4267 offset += N;
4268 }
4269 }
4270 for (size_t i = 0; i < mapSize; ++i) {
4271 const Vector<uint32_t>& vector = map.itemAt(i);
4272 const size_t N = vector.size();
4273 if (N == 0) {
4274 continue;
4275 }
4276 *data++ = htodl(N - 1); // do not count the offset (which is vector's first element)
4277 for (size_t j = 0; j < N; ++j) {
4278 const uint32_t& overlayResID = vector.itemAt(j);
4279 *data++ = htodl(overlayResID);
4280 }
4281 }
4282
4283 return NO_ERROR;
4284}
4285
4286bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
4287 uint32_t* pOriginalCrc, uint32_t* pOverlayCrc)
4288{
4289 const uint32_t* map = (const uint32_t*)idmap;
4290 if (!assertIdmapHeader(map, sizeBytes)) {
4291 return false;
4292 }
4293 *pOriginalCrc = map[1];
4294 *pOverlayCrc = map[2];
4295 return true;
4296}
4297
4298
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004299#ifndef HAVE_ANDROID_OS
4300#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string())
4301
4302#define CHAR16_ARRAY_EQ(constant, var, len) \
4303 ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len))))
4304
Dianne Hackborne17086b2009-06-19 15:13:28 -07004305void print_complex(uint32_t complex, bool isFraction)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004306{
Dianne Hackborne17086b2009-06-19 15:13:28 -07004307 const float MANTISSA_MULT =
4308 1.0f / (1<<Res_value::COMPLEX_MANTISSA_SHIFT);
4309 const float RADIX_MULTS[] = {
4310 1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT,
4311 1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT
4312 };
4313
4314 float value = (complex&(Res_value::COMPLEX_MANTISSA_MASK
4315 <<Res_value::COMPLEX_MANTISSA_SHIFT))
4316 * RADIX_MULTS[(complex>>Res_value::COMPLEX_RADIX_SHIFT)
4317 & Res_value::COMPLEX_RADIX_MASK];
4318 printf("%f", value);
4319
Dianne Hackbornde7faf62009-06-30 13:27:30 -07004320 if (!isFraction) {
Dianne Hackborne17086b2009-06-19 15:13:28 -07004321 switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
4322 case Res_value::COMPLEX_UNIT_PX: printf("px"); break;
4323 case Res_value::COMPLEX_UNIT_DIP: printf("dp"); break;
4324 case Res_value::COMPLEX_UNIT_SP: printf("sp"); break;
4325 case Res_value::COMPLEX_UNIT_PT: printf("pt"); break;
4326 case Res_value::COMPLEX_UNIT_IN: printf("in"); break;
4327 case Res_value::COMPLEX_UNIT_MM: printf("mm"); break;
4328 default: printf(" (unknown unit)"); break;
4329 }
4330 } else {
4331 switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
4332 case Res_value::COMPLEX_UNIT_FRACTION: printf("%%"); break;
4333 case Res_value::COMPLEX_UNIT_FRACTION_PARENT: printf("%%p"); break;
4334 default: printf(" (unknown unit)"); break;
4335 }
4336 }
4337}
4338
Shachar Shemesh9872bf42010-12-20 17:38:33 +02004339// Normalize a string for output
4340String8 ResTable::normalizeForOutput( const char *input )
4341{
4342 String8 ret;
4343 char buff[2];
4344 buff[1] = '\0';
4345
4346 while (*input != '\0') {
4347 switch (*input) {
4348 // All interesting characters are in the ASCII zone, so we are making our own lives
4349 // easier by scanning the string one byte at a time.
4350 case '\\':
4351 ret += "\\\\";
4352 break;
4353 case '\n':
4354 ret += "\\n";
4355 break;
4356 case '"':
4357 ret += "\\\"";
4358 break;
4359 default:
4360 buff[0] = *input;
4361 ret += buff;
4362 break;
4363 }
4364
4365 input++;
4366 }
4367
4368 return ret;
4369}
4370
Dianne Hackbornde7faf62009-06-30 13:27:30 -07004371void ResTable::print_value(const Package* pkg, const Res_value& value) const
4372{
4373 if (value.dataType == Res_value::TYPE_NULL) {
4374 printf("(null)\n");
4375 } else if (value.dataType == Res_value::TYPE_REFERENCE) {
4376 printf("(reference) 0x%08x\n", value.data);
4377 } else if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
4378 printf("(attribute) 0x%08x\n", value.data);
4379 } else if (value.dataType == Res_value::TYPE_STRING) {
4380 size_t len;
Kenny Root780d2a12010-02-22 22:36:26 -08004381 const char* str8 = pkg->header->values.string8At(
Dianne Hackbornde7faf62009-06-30 13:27:30 -07004382 value.data, &len);
Kenny Root780d2a12010-02-22 22:36:26 -08004383 if (str8 != NULL) {
Shachar Shemesh9872bf42010-12-20 17:38:33 +02004384 printf("(string8) \"%s\"\n", normalizeForOutput(str8).string());
Dianne Hackbornde7faf62009-06-30 13:27:30 -07004385 } else {
Kenny Root780d2a12010-02-22 22:36:26 -08004386 const char16_t* str16 = pkg->header->values.stringAt(
4387 value.data, &len);
4388 if (str16 != NULL) {
4389 printf("(string16) \"%s\"\n",
Shachar Shemesh9872bf42010-12-20 17:38:33 +02004390 normalizeForOutput(String8(str16, len).string()).string());
Kenny Root780d2a12010-02-22 22:36:26 -08004391 } else {
4392 printf("(string) null\n");
4393 }
Dianne Hackbornde7faf62009-06-30 13:27:30 -07004394 }
4395 } else if (value.dataType == Res_value::TYPE_FLOAT) {
4396 printf("(float) %g\n", *(const float*)&value.data);
4397 } else if (value.dataType == Res_value::TYPE_DIMENSION) {
4398 printf("(dimension) ");
4399 print_complex(value.data, false);
4400 printf("\n");
4401 } else if (value.dataType == Res_value::TYPE_FRACTION) {
4402 printf("(fraction) ");
4403 print_complex(value.data, true);
4404 printf("\n");
4405 } else if (value.dataType >= Res_value::TYPE_FIRST_COLOR_INT
4406 || value.dataType <= Res_value::TYPE_LAST_COLOR_INT) {
4407 printf("(color) #%08x\n", value.data);
4408 } else if (value.dataType == Res_value::TYPE_INT_BOOLEAN) {
4409 printf("(boolean) %s\n", value.data ? "true" : "false");
4410 } else if (value.dataType >= Res_value::TYPE_FIRST_INT
4411 || value.dataType <= Res_value::TYPE_LAST_INT) {
4412 printf("(int) 0x%08x or %d\n", value.data, value.data);
4413 } else {
4414 printf("(unknown type) t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)\n",
4415 (int)value.dataType, (int)value.data,
4416 (int)value.size, (int)value.res0);
4417 }
4418}
4419
Dianne Hackborne17086b2009-06-19 15:13:28 -07004420void ResTable::print(bool inclValues) const
4421{
4422 if (mError != 0) {
4423 printf("mError=0x%x (%s)\n", mError, strerror(mError));
4424 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004425#if 0
4426 printf("mParams=%c%c-%c%c,\n",
4427 mParams.language[0], mParams.language[1],
4428 mParams.country[0], mParams.country[1]);
4429#endif
4430 size_t pgCount = mPackageGroups.size();
4431 printf("Package Groups (%d)\n", (int)pgCount);
4432 for (size_t pgIndex=0; pgIndex<pgCount; pgIndex++) {
4433 const PackageGroup* pg = mPackageGroups[pgIndex];
4434 printf("Package Group %d id=%d packageCount=%d name=%s\n",
4435 (int)pgIndex, pg->id, (int)pg->packages.size(),
4436 String8(pg->name).string());
4437
4438 size_t pkgCount = pg->packages.size();
4439 for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
4440 const Package* pkg = pg->packages[pkgIndex];
4441 size_t typeCount = pkg->types.size();
4442 printf(" Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex,
4443 pkg->package->id, String8(String16(pkg->package->name)).string(),
4444 (int)typeCount);
4445 for (size_t typeIndex=0; typeIndex<typeCount; typeIndex++) {
4446 const Type* typeConfigs = pkg->getType(typeIndex);
4447 if (typeConfigs == NULL) {
4448 printf(" type %d NULL\n", (int)typeIndex);
4449 continue;
4450 }
4451 const size_t NTC = typeConfigs->configs.size();
4452 printf(" type %d configCount=%d entryCount=%d\n",
4453 (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount);
4454 if (typeConfigs->typeSpecFlags != NULL) {
4455 for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) {
4456 uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
4457 | (0x00ff0000 & ((typeIndex+1)<<16))
4458 | (0x0000ffff & (entryIndex));
4459 resource_name resName;
4460 this->getResourceName(resID, &resName);
4461 printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
4462 resID,
4463 CHAR16_TO_CSTR(resName.package, resName.packageLen),
4464 CHAR16_TO_CSTR(resName.type, resName.typeLen),
4465 CHAR16_TO_CSTR(resName.name, resName.nameLen),
4466 dtohl(typeConfigs->typeSpecFlags[entryIndex]));
4467 }
4468 }
4469 for (size_t configIndex=0; configIndex<NTC; configIndex++) {
4470 const ResTable_type* type = typeConfigs->configs[configIndex];
4471 if ((((uint64_t)type)&0x3) != 0) {
4472 printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type);
4473 continue;
4474 }
Dianne Hackborna53b8282009-07-17 11:13:48 -07004475 char density[16];
4476 uint16_t dval = dtohs(type->config.density);
4477 if (dval == ResTable_config::DENSITY_DEFAULT) {
4478 strcpy(density, "def");
4479 } else if (dval == ResTable_config::DENSITY_NONE) {
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07004480 strcpy(density, "no");
Dianne Hackborna53b8282009-07-17 11:13:48 -07004481 } else {
4482 sprintf(density, "%d", (int)dval);
4483 }
Dianne Hackbornef05e072010-03-01 17:43:39 -08004484 printf(" config %d", (int)configIndex);
4485 if (type->config.mcc != 0) {
4486 printf(" mcc=%d", dtohs(type->config.mcc));
4487 }
4488 if (type->config.mnc != 0) {
4489 printf(" mnc=%d", dtohs(type->config.mnc));
4490 }
4491 if (type->config.locale != 0) {
4492 printf(" lang=%c%c cnt=%c%c",
4493 type->config.language[0] ? type->config.language[0] : '-',
4494 type->config.language[1] ? type->config.language[1] : '-',
4495 type->config.country[0] ? type->config.country[0] : '-',
4496 type->config.country[1] ? type->config.country[1] : '-');
4497 }
4498 if (type->config.screenLayout != 0) {
4499 printf(" sz=%d",
4500 type->config.screenLayout&ResTable_config::MASK_SCREENSIZE);
4501 switch (type->config.screenLayout&ResTable_config::MASK_SCREENSIZE) {
4502 case ResTable_config::SCREENSIZE_SMALL:
4503 printf(" (small)");
4504 break;
4505 case ResTable_config::SCREENSIZE_NORMAL:
4506 printf(" (normal)");
4507 break;
4508 case ResTable_config::SCREENSIZE_LARGE:
4509 printf(" (large)");
4510 break;
Dianne Hackborn14cee9f2010-04-23 17:51:26 -07004511 case ResTable_config::SCREENSIZE_XLARGE:
4512 printf(" (xlarge)");
4513 break;
Dianne Hackbornef05e072010-03-01 17:43:39 -08004514 }
4515 printf(" lng=%d",
4516 type->config.screenLayout&ResTable_config::MASK_SCREENLONG);
4517 switch (type->config.screenLayout&ResTable_config::MASK_SCREENLONG) {
4518 case ResTable_config::SCREENLONG_NO:
4519 printf(" (notlong)");
4520 break;
4521 case ResTable_config::SCREENLONG_YES:
4522 printf(" (long)");
4523 break;
4524 }
4525 }
4526 if (type->config.orientation != 0) {
4527 printf(" orient=%d", type->config.orientation);
4528 switch (type->config.orientation) {
4529 case ResTable_config::ORIENTATION_PORT:
4530 printf(" (port)");
4531 break;
4532 case ResTable_config::ORIENTATION_LAND:
4533 printf(" (land)");
4534 break;
4535 case ResTable_config::ORIENTATION_SQUARE:
4536 printf(" (square)");
4537 break;
4538 }
4539 }
4540 if (type->config.uiMode != 0) {
4541 printf(" type=%d",
4542 type->config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
4543 switch (type->config.uiMode&ResTable_config::MASK_UI_MODE_TYPE) {
4544 case ResTable_config::UI_MODE_TYPE_NORMAL:
4545 printf(" (normal)");
4546 break;
4547 case ResTable_config::UI_MODE_TYPE_CAR:
4548 printf(" (car)");
4549 break;
4550 }
4551 printf(" night=%d",
4552 type->config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
4553 switch (type->config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT) {
4554 case ResTable_config::UI_MODE_NIGHT_NO:
4555 printf(" (no)");
4556 break;
4557 case ResTable_config::UI_MODE_NIGHT_YES:
4558 printf(" (yes)");
4559 break;
4560 }
4561 }
4562 if (dval != 0) {
4563 printf(" density=%s", density);
4564 }
4565 if (type->config.touchscreen != 0) {
4566 printf(" touch=%d", type->config.touchscreen);
4567 switch (type->config.touchscreen) {
4568 case ResTable_config::TOUCHSCREEN_NOTOUCH:
4569 printf(" (notouch)");
4570 break;
4571 case ResTable_config::TOUCHSCREEN_STYLUS:
4572 printf(" (stylus)");
4573 break;
4574 case ResTable_config::TOUCHSCREEN_FINGER:
4575 printf(" (finger)");
4576 break;
4577 }
4578 }
4579 if (type->config.inputFlags != 0) {
4580 printf(" keyhid=%d", type->config.inputFlags&ResTable_config::MASK_KEYSHIDDEN);
4581 switch (type->config.inputFlags&ResTable_config::MASK_KEYSHIDDEN) {
4582 case ResTable_config::KEYSHIDDEN_NO:
4583 printf(" (no)");
4584 break;
4585 case ResTable_config::KEYSHIDDEN_YES:
4586 printf(" (yes)");
4587 break;
4588 case ResTable_config::KEYSHIDDEN_SOFT:
4589 printf(" (soft)");
4590 break;
4591 }
4592 printf(" navhid=%d", type->config.inputFlags&ResTable_config::MASK_NAVHIDDEN);
4593 switch (type->config.inputFlags&ResTable_config::MASK_NAVHIDDEN) {
4594 case ResTable_config::NAVHIDDEN_NO:
4595 printf(" (no)");
4596 break;
4597 case ResTable_config::NAVHIDDEN_YES:
4598 printf(" (yes)");
4599 break;
4600 }
4601 }
4602 if (type->config.keyboard != 0) {
4603 printf(" kbd=%d", type->config.keyboard);
4604 switch (type->config.keyboard) {
4605 case ResTable_config::KEYBOARD_NOKEYS:
4606 printf(" (nokeys)");
4607 break;
4608 case ResTable_config::KEYBOARD_QWERTY:
4609 printf(" (qwerty)");
4610 break;
4611 case ResTable_config::KEYBOARD_12KEY:
4612 printf(" (12key)");
4613 break;
4614 }
4615 }
4616 if (type->config.navigation != 0) {
4617 printf(" nav=%d", type->config.navigation);
4618 switch (type->config.navigation) {
4619 case ResTable_config::NAVIGATION_NONAV:
4620 printf(" (nonav)");
4621 break;
4622 case ResTable_config::NAVIGATION_DPAD:
4623 printf(" (dpad)");
4624 break;
4625 case ResTable_config::NAVIGATION_TRACKBALL:
4626 printf(" (trackball)");
4627 break;
4628 case ResTable_config::NAVIGATION_WHEEL:
4629 printf(" (wheel)");
4630 break;
4631 }
4632 }
4633 if (type->config.screenWidth != 0) {
4634 printf(" w=%d", dtohs(type->config.screenWidth));
4635 }
4636 if (type->config.screenHeight != 0) {
4637 printf(" h=%d", dtohs(type->config.screenHeight));
4638 }
4639 if (type->config.sdkVersion != 0) {
4640 printf(" sdk=%d", dtohs(type->config.sdkVersion));
4641 }
4642 if (type->config.minorVersion != 0) {
4643 printf(" mver=%d", dtohs(type->config.minorVersion));
4644 }
4645 printf("\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004646 size_t entryCount = dtohl(type->entryCount);
4647 uint32_t entriesStart = dtohl(type->entriesStart);
4648 if ((entriesStart&0x3) != 0) {
4649 printf(" NON-INTEGER ResTable_type entriesStart OFFSET: %p\n", (void*)entriesStart);
4650 continue;
4651 }
4652 uint32_t typeSize = dtohl(type->header.size);
4653 if ((typeSize&0x3) != 0) {
4654 printf(" NON-INTEGER ResTable_type header.size: %p\n", (void*)typeSize);
4655 continue;
4656 }
4657 for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
4658
4659 const uint8_t* const end = ((const uint8_t*)type)
4660 + dtohl(type->header.size);
4661 const uint32_t* const eindex = (const uint32_t*)
4662 (((const uint8_t*)type) + dtohs(type->header.headerSize));
4663
4664 uint32_t thisOffset = dtohl(eindex[entryIndex]);
4665 if (thisOffset == ResTable_type::NO_ENTRY) {
4666 continue;
4667 }
4668
4669 uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
4670 | (0x00ff0000 & ((typeIndex+1)<<16))
4671 | (0x0000ffff & (entryIndex));
4672 resource_name resName;
4673 this->getResourceName(resID, &resName);
4674 printf(" resource 0x%08x %s:%s/%s: ", resID,
4675 CHAR16_TO_CSTR(resName.package, resName.packageLen),
4676 CHAR16_TO_CSTR(resName.type, resName.typeLen),
4677 CHAR16_TO_CSTR(resName.name, resName.nameLen));
4678 if ((thisOffset&0x3) != 0) {
4679 printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset);
4680 continue;
4681 }
4682 if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
4683 printf("OFFSET OUT OF BOUNDS: %p+%p (size is %p)\n",
4684 (void*)entriesStart, (void*)thisOffset,
4685 (void*)typeSize);
4686 continue;
4687 }
4688
4689 const ResTable_entry* ent = (const ResTable_entry*)
4690 (((const uint8_t*)type) + entriesStart + thisOffset);
4691 if (((entriesStart + thisOffset)&0x3) != 0) {
4692 printf("NON-INTEGER ResTable_entry OFFSET: %p\n",
4693 (void*)(entriesStart + thisOffset));
4694 continue;
4695 }
Dianne Hackborne17086b2009-06-19 15:13:28 -07004696
Dianne Hackbornde7faf62009-06-30 13:27:30 -07004697 uint16_t esize = dtohs(ent->size);
4698 if ((esize&0x3) != 0) {
4699 printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize);
4700 continue;
4701 }
4702 if ((thisOffset+esize) > typeSize) {
4703 printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n",
4704 (void*)entriesStart, (void*)thisOffset,
4705 (void*)esize, (void*)typeSize);
4706 continue;
4707 }
4708
4709 const Res_value* valuePtr = NULL;
4710 const ResTable_map_entry* bagPtr = NULL;
4711 Res_value value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004712 if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
4713 printf("<bag>");
Dianne Hackbornde7faf62009-06-30 13:27:30 -07004714 bagPtr = (const ResTable_map_entry*)ent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004715 } else {
Dianne Hackbornde7faf62009-06-30 13:27:30 -07004716 valuePtr = (const Res_value*)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004717 (((const uint8_t*)ent) + esize);
Dianne Hackbornde7faf62009-06-30 13:27:30 -07004718 value.copyFrom_dtoh(*valuePtr);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004719 printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
Dianne Hackbornde7faf62009-06-30 13:27:30 -07004720 (int)value.dataType, (int)value.data,
4721 (int)value.size, (int)value.res0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004722 }
4723
4724 if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
4725 printf(" (PUBLIC)");
4726 }
4727 printf("\n");
Dianne Hackborne17086b2009-06-19 15:13:28 -07004728
4729 if (inclValues) {
Dianne Hackbornde7faf62009-06-30 13:27:30 -07004730 if (valuePtr != NULL) {
Dianne Hackborne17086b2009-06-19 15:13:28 -07004731 printf(" ");
Dianne Hackbornde7faf62009-06-30 13:27:30 -07004732 print_value(pkg, value);
4733 } else if (bagPtr != NULL) {
4734 const int N = dtohl(bagPtr->count);
4735 const ResTable_map* mapPtr = (const ResTable_map*)
4736 (((const uint8_t*)ent) + esize);
4737 printf(" Parent=0x%08x, Count=%d\n",
4738 dtohl(bagPtr->parent.ident), N);
4739 for (int i=0; i<N; i++) {
4740 printf(" #%i (Key=0x%08x): ",
4741 i, dtohl(mapPtr->name.ident));
4742 value.copyFrom_dtoh(mapPtr->value);
4743 print_value(pkg, value);
4744 const size_t size = dtohs(mapPtr->value.size);
4745 mapPtr = (ResTable_map*)(((const uint8_t*)mapPtr)
4746 + size + sizeof(*mapPtr)-sizeof(mapPtr->value));
Dianne Hackborne17086b2009-06-19 15:13:28 -07004747 }
4748 }
4749 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004750 }
4751 }
4752 }
4753 }
4754 }
4755}
4756
4757#endif // HAVE_ANDROID_OS
4758
4759} // namespace android