blob: 2ad2e76cc696fe113771a80be1e88361c744dc5a [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
Dan Albert1b4f3162015-04-07 18:43:15 -070020#include <ctype.h>
21#include <memory.h>
22#include <stddef.h>
23#include <stdint.h>
24#include <stdlib.h>
25#include <string.h>
26
Adam Lesinskib7e1ce02016-04-11 20:03:01 -070027#include <algorithm>
Dan Albert1b4f3162015-04-07 18:43:15 -070028#include <limits>
Mårten Kongstad67d5c932018-05-25 15:58:17 +020029#include <map>
Adam Lesinskiff5808d2016-02-23 17:49:53 -080030#include <memory>
Mårten Kongstad67d5c932018-05-25 15:58:17 +020031#include <set>
Dan Albert1b4f3162015-04-07 18:43:15 -070032#include <type_traits>
33
Mårten Kongstade0930d32018-10-18 14:50:15 +020034#include <android-base/macros.h>
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -070035#include <androidfw/ByteBucketArray.h>
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080036#include <androidfw/ResourceTypes.h>
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -070037#include <androidfw/TypeWrappers.h>
Steven Morelandfb7952f2018-02-23 14:58:50 -080038#include <cutils/atomic.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039#include <utils/ByteOrder.h>
40#include <utils/Debug.h>
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080041#include <utils/Log.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042#include <utils/String16.h>
43#include <utils/String8.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044
Dan Albert1b4f3162015-04-07 18:43:15 -070045#ifdef __ANDROID__
Andreas Gampe2204f0b2014-10-21 23:04:54 -070046#include <binder/TextOutput.h>
47#endif
48
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049#ifndef INT32_MAX
50#define INT32_MAX ((int32_t)(2147483647))
51#endif
52
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053namespace android {
54
Elliott Hughes59cbe8d2015-07-29 17:49:27 -070055#if defined(_WIN32)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056#undef nhtol
57#undef htonl
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058#define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) )
59#define htonl(x) ntohl(x)
60#define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) )
61#define htons(x) ntohs(x)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062#endif
63
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -070064#define IDMAP_MAGIC 0x504D4449
Mårten Kongstad57f4b772011-03-17 14:13:41 +010065
Adam Lesinskide898ff2014-01-29 18:20:45 -080066#define APP_PACKAGE_ID 0x7f
67#define SYS_PACKAGE_ID 0x01
68
Andreas Gampe2204f0b2014-10-21 23:04:54 -070069static const bool kDebugStringPoolNoisy = false;
70static const bool kDebugXMLNoisy = false;
71static const bool kDebugTableNoisy = false;
72static const bool kDebugTableGetEntry = false;
73static const bool kDebugTableSuperNoisy = false;
74static const bool kDebugLoadTableNoisy = false;
75static const bool kDebugLoadTableSuperNoisy = false;
76static const bool kDebugTableTheme = false;
77static const bool kDebugResXMLTree = false;
78static const bool kDebugLibNoisy = false;
79
80// TODO: This code uses 0xFFFFFFFF converted to bag_set* as a sentinel value. This is bad practice.
81
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082// Standard C isspace() is only required to look at the low byte of its input, so
83// produces incorrect results for UTF-16 characters. For safety's sake, assume that
84// any high-byte UTF-16 code point is not whitespace.
85inline int isspace16(char16_t c) {
86 return (c < 0x0080 && isspace(c));
87}
88
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -070089template<typename T>
90inline static T max(T a, T b) {
91 return a > b ? a : b;
92}
93
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094// range checked; guaranteed to NUL-terminate within the stated number of available slots
95// NOTE: if this truncates the dst string due to running out of space, no attempt is
96// made to avoid splitting surrogate pairs.
Adam Lesinski4bf58102014-11-03 11:21:19 -080097static void strcpy16_dtoh(char16_t* dst, const uint16_t* src, size_t avail)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098{
Dan Albertf348c152014-09-08 18:28:00 -070099 char16_t* last = dst + avail - 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 while (*src && (dst < last)) {
Adam Lesinski4bf58102014-11-03 11:21:19 -0800101 char16_t s = dtohs(static_cast<char16_t>(*src));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 *dst++ = s;
103 src++;
104 }
105 *dst = 0;
106}
107
108static status_t validate_chunk(const ResChunk_header* chunk,
109 size_t minSize,
110 const uint8_t* dataEnd,
111 const char* name)
112{
113 const uint16_t headerSize = dtohs(chunk->headerSize);
114 const uint32_t size = dtohl(chunk->size);
115
116 if (headerSize >= minSize) {
117 if (headerSize <= size) {
118 if (((headerSize|size)&0x3) == 0) {
Adam Lesinski7322ea72014-05-14 11:43:26 -0700119 if ((size_t)size <= (size_t)(dataEnd-((const uint8_t*)chunk))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 return NO_ERROR;
121 }
Patrik Bannura443dd932014-02-12 13:38:54 +0100122 ALOGW("%s data size 0x%x extends beyond resource end %p.",
123 name, size, (void*)(dataEnd-((const uint8_t*)chunk)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 return BAD_TYPE;
125 }
Steve Block8564c8d2012-01-05 23:22:43 +0000126 ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 name, (int)size, (int)headerSize);
128 return BAD_TYPE;
129 }
Patrik Bannura443dd932014-02-12 13:38:54 +0100130 ALOGW("%s size 0x%x is smaller than header size 0x%x.",
131 name, size, headerSize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 return BAD_TYPE;
133 }
Adam Lesinskide898ff2014-01-29 18:20:45 -0800134 ALOGW("%s header size 0x%04x is too small.",
Patrik Bannura443dd932014-02-12 13:38:54 +0100135 name, headerSize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 return BAD_TYPE;
137}
138
Narayan Kamath6381dd42014-03-03 17:12:03 +0000139static void fill9patchOffsets(Res_png_9patch* patch) {
140 patch->xDivsOffset = sizeof(Res_png_9patch);
141 patch->yDivsOffset = patch->xDivsOffset + (patch->numXDivs * sizeof(int32_t));
142 patch->colorsOffset = patch->yDivsOffset + (patch->numYDivs * sizeof(int32_t));
143}
144
Adam Lesinski7ad11102016-10-28 16:39:15 -0700145void Res_value::copyFrom_dtoh(const Res_value& src)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146{
147 size = dtohs(src.size);
148 res0 = src.res0;
149 dataType = src.dataType;
150 data = dtohl(src.data);
151}
152
153void Res_png_9patch::deviceToFile()
154{
Narayan Kamath6381dd42014-03-03 17:12:03 +0000155 int32_t* xDivs = getXDivs();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 for (int i = 0; i < numXDivs; i++) {
157 xDivs[i] = htonl(xDivs[i]);
158 }
Narayan Kamath6381dd42014-03-03 17:12:03 +0000159 int32_t* yDivs = getYDivs();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 for (int i = 0; i < numYDivs; i++) {
161 yDivs[i] = htonl(yDivs[i]);
162 }
163 paddingLeft = htonl(paddingLeft);
164 paddingRight = htonl(paddingRight);
165 paddingTop = htonl(paddingTop);
166 paddingBottom = htonl(paddingBottom);
Narayan Kamath6381dd42014-03-03 17:12:03 +0000167 uint32_t* colors = getColors();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 for (int i=0; i<numColors; i++) {
169 colors[i] = htonl(colors[i]);
170 }
171}
172
173void Res_png_9patch::fileToDevice()
174{
Narayan Kamath6381dd42014-03-03 17:12:03 +0000175 int32_t* xDivs = getXDivs();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 for (int i = 0; i < numXDivs; i++) {
177 xDivs[i] = ntohl(xDivs[i]);
178 }
Narayan Kamath6381dd42014-03-03 17:12:03 +0000179 int32_t* yDivs = getYDivs();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 for (int i = 0; i < numYDivs; i++) {
181 yDivs[i] = ntohl(yDivs[i]);
182 }
183 paddingLeft = ntohl(paddingLeft);
184 paddingRight = ntohl(paddingRight);
185 paddingTop = ntohl(paddingTop);
186 paddingBottom = ntohl(paddingBottom);
Narayan Kamath6381dd42014-03-03 17:12:03 +0000187 uint32_t* colors = getColors();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 for (int i=0; i<numColors; i++) {
189 colors[i] = ntohl(colors[i]);
190 }
191}
192
Narayan Kamath6381dd42014-03-03 17:12:03 +0000193size_t Res_png_9patch::serializedSize() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194{
195 // The size of this struct is 32 bytes on the 32-bit target system
196 // 4 * int8_t
197 // 4 * int32_t
Narayan Kamath6381dd42014-03-03 17:12:03 +0000198 // 3 * uint32_t
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 return 32
200 + numXDivs * sizeof(int32_t)
201 + numYDivs * sizeof(int32_t)
202 + numColors * sizeof(uint32_t);
203}
204
Narayan Kamath6381dd42014-03-03 17:12:03 +0000205void* Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs,
206 const int32_t* yDivs, const uint32_t* colors)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207{
The Android Open Source Project4df24232009-03-05 14:34:35 -0800208 // Use calloc since we're going to leave a few holes in the data
209 // and want this to run cleanly under valgrind
Narayan Kamath6381dd42014-03-03 17:12:03 +0000210 void* newData = calloc(1, patch.serializedSize());
211 serialize(patch, xDivs, yDivs, colors, newData);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 return newData;
213}
214
Narayan Kamath6381dd42014-03-03 17:12:03 +0000215void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs,
216 const int32_t* yDivs, const uint32_t* colors, void* outData)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217{
Narayan Kamath6381dd42014-03-03 17:12:03 +0000218 uint8_t* data = (uint8_t*) outData;
219 memcpy(data, &patch.wasDeserialized, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors
220 memcpy(data + 12, &patch.paddingLeft, 16); // copy paddingXXXX
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 data += 32;
222
Narayan Kamath6381dd42014-03-03 17:12:03 +0000223 memcpy(data, xDivs, patch.numXDivs * sizeof(int32_t));
224 data += patch.numXDivs * sizeof(int32_t);
225 memcpy(data, yDivs, patch.numYDivs * sizeof(int32_t));
226 data += patch.numYDivs * sizeof(int32_t);
227 memcpy(data, colors, patch.numColors * sizeof(uint32_t));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228
Narayan Kamath6381dd42014-03-03 17:12:03 +0000229 fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230}
231
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700232static bool assertIdmapHeader(const void* idmap, size_t size) {
233 if (reinterpret_cast<uintptr_t>(idmap) & 0x03) {
234 ALOGE("idmap: header is not word aligned");
Mårten Kongstad57f4b772011-03-17 14:13:41 +0100235 return false;
236 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700237
238 if (size < ResTable::IDMAP_HEADER_SIZE_BYTES) {
239 ALOGW("idmap: header too small (%d bytes)", (uint32_t) size);
240 return false;
241 }
242
243 const uint32_t magic = htodl(*reinterpret_cast<const uint32_t*>(idmap));
244 if (magic != IDMAP_MAGIC) {
245 ALOGW("idmap: no magic found in header (is 0x%08x, expected 0x%08x)",
246 magic, IDMAP_MAGIC);
247 return false;
248 }
249
250 const uint32_t version = htodl(*(reinterpret_cast<const uint32_t*>(idmap) + 1));
Mårten Kongstad42ebcb82017-03-28 15:30:21 +0200251 if (version != ResTable::IDMAP_CURRENT_VERSION) {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700252 // We are strict about versions because files with this format are
253 // auto-generated and don't need backwards compatibility.
254 ALOGW("idmap: version mismatch in header (is 0x%08x, expected 0x%08x)",
Mårten Kongstad42ebcb82017-03-28 15:30:21 +0200255 version, ResTable::IDMAP_CURRENT_VERSION);
Mårten Kongstad57f4b772011-03-17 14:13:41 +0100256 return false;
257 }
258 return true;
259}
260
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700261class IdmapEntries {
262public:
263 IdmapEntries() : mData(NULL) {}
Mårten Kongstad57f4b772011-03-17 14:13:41 +0100264
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700265 bool hasEntries() const {
266 if (mData == NULL) {
267 return false;
268 }
Mårten Kongstad57f4b772011-03-17 14:13:41 +0100269
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700270 return (dtohs(*mData) > 0);
271 }
Mårten Kongstad57f4b772011-03-17 14:13:41 +0100272
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700273 size_t byteSize() const {
274 if (mData == NULL) {
275 return 0;
276 }
277 uint16_t entryCount = dtohs(mData[2]);
278 return (sizeof(uint16_t) * 4) + (sizeof(uint32_t) * static_cast<size_t>(entryCount));
Mårten Kongstad57f4b772011-03-17 14:13:41 +0100279 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700280
281 uint8_t targetTypeId() const {
282 if (mData == NULL) {
283 return 0;
284 }
285 return dtohs(mData[0]);
Mårten Kongstad48d22322014-01-31 14:43:27 +0100286 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700287
288 uint8_t overlayTypeId() const {
289 if (mData == NULL) {
290 return 0;
291 }
292 return dtohs(mData[1]);
Mårten Kongstad48d22322014-01-31 14:43:27 +0100293 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700294
295 status_t setTo(const void* entryHeader, size_t size) {
296 if (reinterpret_cast<uintptr_t>(entryHeader) & 0x03) {
297 ALOGE("idmap: entry header is not word aligned");
Mårten Kongstad48d22322014-01-31 14:43:27 +0100298 return UNKNOWN_ERROR;
299 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700300
301 if (size < sizeof(uint16_t) * 4) {
302 ALOGE("idmap: entry header is too small (%u bytes)", (uint32_t) size);
303 return UNKNOWN_ERROR;
304 }
305
306 const uint16_t* header = reinterpret_cast<const uint16_t*>(entryHeader);
307 const uint16_t targetTypeId = dtohs(header[0]);
308 const uint16_t overlayTypeId = dtohs(header[1]);
309 if (targetTypeId == 0 || overlayTypeId == 0 || targetTypeId > 255 || overlayTypeId > 255) {
310 ALOGE("idmap: invalid type map (%u -> %u)", targetTypeId, overlayTypeId);
311 return UNKNOWN_ERROR;
312 }
313
314 uint16_t entryCount = dtohs(header[2]);
315 if (size < sizeof(uint32_t) * (entryCount + 2)) {
316 ALOGE("idmap: too small (%u bytes) for the number of entries (%u)",
317 (uint32_t) size, (uint32_t) entryCount);
318 return UNKNOWN_ERROR;
319 }
320 mData = header;
321 return NO_ERROR;
Mårten Kongstad57f4b772011-03-17 14:13:41 +0100322 }
Mårten Kongstad48d22322014-01-31 14:43:27 +0100323
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700324 status_t lookup(uint16_t entryId, uint16_t* outEntryId) const {
325 uint16_t entryCount = dtohs(mData[2]);
326 uint16_t offset = dtohs(mData[3]);
327
328 if (entryId < offset) {
329 // The entry is not present in this idmap
330 return BAD_INDEX;
331 }
332
333 entryId -= offset;
334
335 if (entryId >= entryCount) {
336 // The entry is not present in this idmap
337 return BAD_INDEX;
338 }
339
340 // It is safe to access the type here without checking the size because
341 // we have checked this when it was first loaded.
342 const uint32_t* entries = reinterpret_cast<const uint32_t*>(mData) + 2;
343 uint32_t mappedEntry = dtohl(entries[entryId]);
344 if (mappedEntry == 0xffffffff) {
345 // This entry is not present in this idmap
346 return BAD_INDEX;
347 }
348 *outEntryId = static_cast<uint16_t>(mappedEntry);
349 return NO_ERROR;
350 }
351
352private:
353 const uint16_t* mData;
354};
355
356status_t parseIdmap(const void* idmap, size_t size, uint8_t* outPackageId, KeyedVector<uint8_t, IdmapEntries>* outMap) {
357 if (!assertIdmapHeader(idmap, size)) {
Mårten Kongstad48d22322014-01-31 14:43:27 +0100358 return UNKNOWN_ERROR;
359 }
Mårten Kongstad48d22322014-01-31 14:43:27 +0100360
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700361 size -= ResTable::IDMAP_HEADER_SIZE_BYTES;
362 if (size < sizeof(uint16_t) * 2) {
363 ALOGE("idmap: too small to contain any mapping");
364 return UNKNOWN_ERROR;
365 }
366
367 const uint16_t* data = reinterpret_cast<const uint16_t*>(
368 reinterpret_cast<const uint8_t*>(idmap) + ResTable::IDMAP_HEADER_SIZE_BYTES);
369
370 uint16_t targetPackageId = dtohs(*(data++));
371 if (targetPackageId == 0 || targetPackageId > 255) {
372 ALOGE("idmap: target package ID is invalid (%02x)", targetPackageId);
373 return UNKNOWN_ERROR;
374 }
375
376 uint16_t mapCount = dtohs(*(data++));
377 if (mapCount == 0) {
378 ALOGE("idmap: no mappings");
379 return UNKNOWN_ERROR;
380 }
381
382 if (mapCount > 255) {
383 ALOGW("idmap: too many mappings. Only 255 are possible but %u are present", (uint32_t) mapCount);
384 }
385
386 while (size > sizeof(uint16_t) * 4) {
387 IdmapEntries entries;
388 status_t err = entries.setTo(data, size);
389 if (err != NO_ERROR) {
390 return err;
391 }
392
393 ssize_t index = outMap->add(entries.overlayTypeId(), entries);
394 if (index < 0) {
395 return NO_MEMORY;
396 }
397
398 data += entries.byteSize() / sizeof(uint16_t);
399 size -= entries.byteSize();
400 }
401
402 if (outPackageId != NULL) {
403 *outPackageId = static_cast<uint8_t>(targetPackageId);
404 }
Mårten Kongstad57f4b772011-03-17 14:13:41 +0100405 return NO_ERROR;
406}
407
Narayan Kamath6381dd42014-03-03 17:12:03 +0000408Res_png_9patch* Res_png_9patch::deserialize(void* inData)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409{
Narayan Kamath6381dd42014-03-03 17:12:03 +0000410
411 Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(inData);
412 patch->wasDeserialized = true;
413 fill9patchOffsets(patch);
414
415 return patch;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416}
417
418// --------------------------------------------------------------------
419// --------------------------------------------------------------------
420// --------------------------------------------------------------------
421
422ResStringPool::ResStringPool()
Kenny Root19138462009-12-04 09:38:48 -0800423 : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424{
425}
426
427ResStringPool::ResStringPool(const void* data, size_t size, bool copyData)
Kenny Root19138462009-12-04 09:38:48 -0800428 : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429{
430 setTo(data, size, copyData);
431}
432
433ResStringPool::~ResStringPool()
434{
435 uninit();
436}
437
Adam Lesinskide898ff2014-01-29 18:20:45 -0800438void ResStringPool::setToEmpty()
439{
440 uninit();
441
442 mOwnedData = calloc(1, sizeof(ResStringPool_header));
443 ResStringPool_header* header = (ResStringPool_header*) mOwnedData;
444 mSize = 0;
445 mEntries = NULL;
446 mStrings = NULL;
447 mStringPoolSize = 0;
448 mEntryStyles = NULL;
449 mStyles = NULL;
450 mStylePoolSize = 0;
451 mHeader = (const ResStringPool_header*) header;
452}
453
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
455{
456 if (!data || !size) {
457 return (mError=BAD_TYPE);
458 }
459
460 uninit();
461
ye46df9d2018-04-05 17:57:27 -0700462 // The chunk must be at least the size of the string pool header.
463 if (size < sizeof(ResStringPool_header)) {
Ryan Mitchellf05f47b2018-05-21 13:59:23 -0700464 ALOGW("Bad string block: data size %zu is too small to be a string block", size);
ye46df9d2018-04-05 17:57:27 -0700465 return (mError=BAD_TYPE);
466 }
467
468 // The data is at least as big as a ResChunk_header, so we can safely validate the other
469 // header fields.
470 // `data + size` is safe because the source of `size` comes from the kernel/filesystem.
471 if (validate_chunk(reinterpret_cast<const ResChunk_header*>(data), sizeof(ResStringPool_header),
472 reinterpret_cast<const uint8_t*>(data) + size,
473 "ResStringPool_header") != NO_ERROR) {
Ryan Mitchellf05f47b2018-05-21 13:59:23 -0700474 ALOGW("Bad string block: malformed block dimensions");
ye46df9d2018-04-05 17:57:27 -0700475 return (mError=BAD_TYPE);
476 }
477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 const bool notDeviceEndian = htods(0xf0) != 0xf0;
479
480 if (copyData || notDeviceEndian) {
481 mOwnedData = malloc(size);
482 if (mOwnedData == NULL) {
483 return (mError=NO_MEMORY);
484 }
485 memcpy(mOwnedData, data, size);
486 data = mOwnedData;
487 }
488
ye46df9d2018-04-05 17:57:27 -0700489 // The size has been checked, so it is safe to read the data in the ResStringPool_header
490 // data structure.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 mHeader = (const ResStringPool_header*)data;
492
493 if (notDeviceEndian) {
494 ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader);
495 h->header.headerSize = dtohs(mHeader->header.headerSize);
496 h->header.type = dtohs(mHeader->header.type);
497 h->header.size = dtohl(mHeader->header.size);
498 h->stringCount = dtohl(mHeader->stringCount);
499 h->styleCount = dtohl(mHeader->styleCount);
500 h->flags = dtohl(mHeader->flags);
501 h->stringsStart = dtohl(mHeader->stringsStart);
502 h->stylesStart = dtohl(mHeader->stylesStart);
503 }
504
505 if (mHeader->header.headerSize > mHeader->header.size
506 || mHeader->header.size > size) {
Steve Block8564c8d2012-01-05 23:22:43 +0000507 ALOGW("Bad string block: header size %d or total size %d is larger than data size %d\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 (int)mHeader->header.headerSize, (int)mHeader->header.size, (int)size);
509 return (mError=BAD_TYPE);
510 }
511 mSize = mHeader->header.size;
512 mEntries = (const uint32_t*)
513 (((const uint8_t*)data)+mHeader->header.headerSize);
514
515 if (mHeader->stringCount > 0) {
516 if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow?
517 || (mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t)))
518 > size) {
Steve Block8564c8d2012-01-05 23:22:43 +0000519 ALOGW("Bad string block: entry of %d items extends past data size %d\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 (int)(mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))),
521 (int)size);
522 return (mError=BAD_TYPE);
523 }
Kenny Root19138462009-12-04 09:38:48 -0800524
525 size_t charSize;
526 if (mHeader->flags&ResStringPool_header::UTF8_FLAG) {
527 charSize = sizeof(uint8_t);
Kenny Root19138462009-12-04 09:38:48 -0800528 } else {
Adam Lesinski4bf58102014-11-03 11:21:19 -0800529 charSize = sizeof(uint16_t);
Kenny Root19138462009-12-04 09:38:48 -0800530 }
531
Adam Lesinskif28d5052014-07-25 15:25:04 -0700532 // There should be at least space for the smallest string
533 // (2 bytes length, null terminator).
534 if (mHeader->stringsStart >= (mSize - sizeof(uint16_t))) {
Steve Block8564c8d2012-01-05 23:22:43 +0000535 ALOGW("Bad string block: string pool starts at %d, after total size %d\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 (int)mHeader->stringsStart, (int)mHeader->header.size);
537 return (mError=BAD_TYPE);
538 }
Adam Lesinskif28d5052014-07-25 15:25:04 -0700539
540 mStrings = (const void*)
541 (((const uint8_t*)data) + mHeader->stringsStart);
542
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 if (mHeader->styleCount == 0) {
Adam Lesinskif28d5052014-07-25 15:25:04 -0700544 mStringPoolSize = (mSize - mHeader->stringsStart) / charSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 } else {
Kenny Root5e4d9a02010-06-08 12:34:43 -0700546 // check invariant: styles starts before end of data
Adam Lesinskif28d5052014-07-25 15:25:04 -0700547 if (mHeader->stylesStart >= (mSize - sizeof(uint16_t))) {
Steve Block8564c8d2012-01-05 23:22:43 +0000548 ALOGW("Bad style block: style block starts at %d past data size of %d\n",
Kenny Root5e4d9a02010-06-08 12:34:43 -0700549 (int)mHeader->stylesStart, (int)mHeader->header.size);
550 return (mError=BAD_TYPE);
551 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 // check invariant: styles follow the strings
553 if (mHeader->stylesStart <= mHeader->stringsStart) {
Steve Block8564c8d2012-01-05 23:22:43 +0000554 ALOGW("Bad style block: style block starts at %d, before strings at %d\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 (int)mHeader->stylesStart, (int)mHeader->stringsStart);
556 return (mError=BAD_TYPE);
557 }
558 mStringPoolSize =
Kenny Root19138462009-12-04 09:38:48 -0800559 (mHeader->stylesStart-mHeader->stringsStart)/charSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 }
561
562 // check invariant: stringCount > 0 requires a string pool to exist
563 if (mStringPoolSize == 0) {
Steve Block8564c8d2012-01-05 23:22:43 +0000564 ALOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 return (mError=BAD_TYPE);
566 }
567
568 if (notDeviceEndian) {
569 size_t i;
570 uint32_t* e = const_cast<uint32_t*>(mEntries);
571 for (i=0; i<mHeader->stringCount; i++) {
572 e[i] = dtohl(mEntries[i]);
573 }
Kenny Root19138462009-12-04 09:38:48 -0800574 if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) {
Adam Lesinski4bf58102014-11-03 11:21:19 -0800575 const uint16_t* strings = (const uint16_t*)mStrings;
576 uint16_t* s = const_cast<uint16_t*>(strings);
Kenny Root19138462009-12-04 09:38:48 -0800577 for (i=0; i<mStringPoolSize; i++) {
578 s[i] = dtohs(strings[i]);
579 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 }
581 }
582
Kenny Root19138462009-12-04 09:38:48 -0800583 if ((mHeader->flags&ResStringPool_header::UTF8_FLAG &&
584 ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) ||
Adam Lesinski666b6fb2016-04-21 10:05:06 -0700585 (!(mHeader->flags&ResStringPool_header::UTF8_FLAG) &&
Adam Lesinski4bf58102014-11-03 11:21:19 -0800586 ((uint16_t*)mStrings)[mStringPoolSize-1] != 0)) {
Steve Block8564c8d2012-01-05 23:22:43 +0000587 ALOGW("Bad string block: last string is not 0-terminated\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 return (mError=BAD_TYPE);
589 }
590 } else {
591 mStrings = NULL;
592 mStringPoolSize = 0;
593 }
594
595 if (mHeader->styleCount > 0) {
596 mEntryStyles = mEntries + mHeader->stringCount;
597 // invariant: integer overflow in calculating mEntryStyles
598 if (mEntryStyles < mEntries) {
Steve Block8564c8d2012-01-05 23:22:43 +0000599 ALOGW("Bad string block: integer overflow finding styles\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 return (mError=BAD_TYPE);
601 }
602
603 if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) {
Steve Block8564c8d2012-01-05 23:22:43 +0000604 ALOGW("Bad string block: entry of %d styles extends past data size %d\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605 (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader),
606 (int)size);
607 return (mError=BAD_TYPE);
608 }
609 mStyles = (const uint32_t*)
610 (((const uint8_t*)data)+mHeader->stylesStart);
611 if (mHeader->stylesStart >= mHeader->header.size) {
Steve Block8564c8d2012-01-05 23:22:43 +0000612 ALOGW("Bad string block: style pool starts %d, after total size %d\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 (int)mHeader->stylesStart, (int)mHeader->header.size);
614 return (mError=BAD_TYPE);
615 }
616 mStylePoolSize =
617 (mHeader->header.size-mHeader->stylesStart)/sizeof(uint32_t);
618
619 if (notDeviceEndian) {
620 size_t i;
621 uint32_t* e = const_cast<uint32_t*>(mEntryStyles);
622 for (i=0; i<mHeader->styleCount; i++) {
623 e[i] = dtohl(mEntryStyles[i]);
624 }
625 uint32_t* s = const_cast<uint32_t*>(mStyles);
626 for (i=0; i<mStylePoolSize; i++) {
627 s[i] = dtohl(mStyles[i]);
628 }
629 }
630
631 const ResStringPool_span endSpan = {
632 { htodl(ResStringPool_span::END) },
633 htodl(ResStringPool_span::END), htodl(ResStringPool_span::END)
634 };
635 if (memcmp(&mStyles[mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t))],
636 &endSpan, sizeof(endSpan)) != 0) {
Steve Block8564c8d2012-01-05 23:22:43 +0000637 ALOGW("Bad string block: last style is not 0xFFFFFFFF-terminated\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 return (mError=BAD_TYPE);
639 }
640 } else {
641 mEntryStyles = NULL;
642 mStyles = NULL;
643 mStylePoolSize = 0;
644 }
645
646 return (mError=NO_ERROR);
647}
648
649status_t ResStringPool::getError() const
650{
651 return mError;
652}
653
654void ResStringPool::uninit()
655{
656 mError = NO_INIT;
Kenny Root19138462009-12-04 09:38:48 -0800657 if (mHeader != NULL && mCache != NULL) {
658 for (size_t x = 0; x < mHeader->stringCount; x++) {
659 if (mCache[x] != NULL) {
660 free(mCache[x]);
661 mCache[x] = NULL;
662 }
663 }
664 free(mCache);
665 mCache = NULL;
666 }
Chris Dearmana1d82ff32012-10-08 12:22:02 -0700667 if (mOwnedData) {
668 free(mOwnedData);
669 mOwnedData = NULL;
670 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671}
672
Kenny Root300ba682010-11-09 14:37:23 -0800673/**
674 * Strings in UTF-16 format have length indicated by a length encoded in the
675 * stored data. It is either 1 or 2 characters of length data. This allows a
676 * maximum length of 0x7FFFFFF (2147483647 bytes), but if you're storing that
677 * much data in a string, you're abusing them.
678 *
679 * If the high bit is set, then there are two characters or 4 bytes of length
680 * data encoded. In that case, drop the high bit of the first character and
681 * add it together with the next character.
682 */
683static inline size_t
Adam Lesinski4bf58102014-11-03 11:21:19 -0800684decodeLength(const uint16_t** str)
Kenny Root300ba682010-11-09 14:37:23 -0800685{
686 size_t len = **str;
687 if ((len & 0x8000) != 0) {
688 (*str)++;
689 len = ((len & 0x7FFF) << 16) | **str;
690 }
691 (*str)++;
692 return len;
693}
Kenny Root19138462009-12-04 09:38:48 -0800694
Kenny Root300ba682010-11-09 14:37:23 -0800695/**
696 * Strings in UTF-8 format have length indicated by a length encoded in the
697 * stored data. It is either 1 or 2 characters of length data. This allows a
698 * maximum length of 0x7FFF (32767 bytes), but you should consider storing
699 * text in another way if you're using that much data in a single string.
700 *
701 * If the high bit is set, then there are two characters or 2 bytes of length
702 * data encoded. In that case, drop the high bit of the first character and
703 * add it together with the next character.
704 */
705static inline size_t
706decodeLength(const uint8_t** str)
707{
708 size_t len = **str;
709 if ((len & 0x80) != 0) {
710 (*str)++;
711 len = ((len & 0x7F) << 8) | **str;
712 }
713 (*str)++;
714 return len;
715}
716
Dan Albertf348c152014-09-08 18:28:00 -0700717const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718{
719 if (mError == NO_ERROR && idx < mHeader->stringCount) {
Kenny Root19138462009-12-04 09:38:48 -0800720 const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0;
Adam Lesinski4bf58102014-11-03 11:21:19 -0800721 const uint32_t off = mEntries[idx]/(isUTF8?sizeof(uint8_t):sizeof(uint16_t));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 if (off < (mStringPoolSize-1)) {
Kenny Root19138462009-12-04 09:38:48 -0800723 if (!isUTF8) {
Adam Lesinski4bf58102014-11-03 11:21:19 -0800724 const uint16_t* strings = (uint16_t*)mStrings;
725 const uint16_t* str = strings+off;
Kenny Root300ba682010-11-09 14:37:23 -0800726
727 *u16len = decodeLength(&str);
728 if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) {
Vishwath Mohan6521a1b2015-03-11 16:08:37 -0700729 // Reject malformed (non null-terminated) strings
730 if (str[*u16len] != 0x0000) {
731 ALOGW("Bad string block: string #%d is not null-terminated",
732 (int)idx);
733 return NULL;
734 }
Adam Lesinski4bf58102014-11-03 11:21:19 -0800735 return reinterpret_cast<const char16_t*>(str);
Kenny Root19138462009-12-04 09:38:48 -0800736 } else {
Steve Block8564c8d2012-01-05 23:22:43 +0000737 ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
Kenny Root300ba682010-11-09 14:37:23 -0800738 (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize);
Kenny Root19138462009-12-04 09:38:48 -0800739 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 } else {
Kenny Root19138462009-12-04 09:38:48 -0800741 const uint8_t* strings = (uint8_t*)mStrings;
Kenny Root300ba682010-11-09 14:37:23 -0800742 const uint8_t* u8str = strings+off;
743
744 *u16len = decodeLength(&u8str);
745 size_t u8len = decodeLength(&u8str);
746
747 // encLen must be less than 0x7FFF due to encoding.
748 if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) {
Kenny Root19138462009-12-04 09:38:48 -0800749 AutoMutex lock(mDecodeLock);
Kenny Root300ba682010-11-09 14:37:23 -0800750
Ryan Mitchell2ad530d2018-03-29 15:49:10 -0700751 if (mCache != NULL && mCache[idx] != NULL) {
752 return mCache[idx];
753 }
754
755 // Retrieve the actual length of the utf8 string if the
756 // encoded length was truncated
757 if (stringDecodeAt(idx, u8str, u8len, &u8len) == NULL) {
758 return NULL;
759 }
760
761 // Since AAPT truncated lengths longer than 0x7FFF, check
762 // that the bits that remain after truncation at least match
763 // the bits of the actual length
764 ssize_t actualLen = utf8_to_utf16_length(u8str, u8len);
765 if (actualLen < 0 || ((size_t)actualLen & 0x7FFF) != *u16len) {
766 ALOGW("Bad string block: string #%lld decoded length is not correct "
767 "%lld vs %llu\n",
768 (long long)idx, (long long)actualLen, (long long)*u16len);
769 return NULL;
770 }
771
772 *u16len = (size_t) actualLen;
773 char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t));
774 if (!u16str) {
775 ALOGW("No memory when trying to allocate decode cache for string #%d\n",
776 (int)idx);
777 return NULL;
778 }
779
780 utf8_to_utf16(u8str, u8len, u16str, *u16len + 1);
781
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700782 if (mCache == NULL) {
Elliott Hughesba3fe562015-08-12 14:49:53 -0700783#ifndef __ANDROID__
Andreas Gampe2204f0b2014-10-21 23:04:54 -0700784 if (kDebugStringPoolNoisy) {
785 ALOGI("CREATING STRING CACHE OF %zu bytes",
Ryan Mitchell2ad530d2018-03-29 15:49:10 -0700786 mHeader->stringCount*sizeof(char16_t**));
Andreas Gampe2204f0b2014-10-21 23:04:54 -0700787 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700788#else
789 // We do not want to be in this case when actually running Android.
Andreas Gampe25df5fb2014-11-07 22:24:57 -0800790 ALOGW("CREATING STRING CACHE OF %zu bytes",
791 static_cast<size_t>(mHeader->stringCount*sizeof(char16_t**)));
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700792#endif
George Burgess IVe8efec52016-10-11 15:42:29 -0700793 mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t*));
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700794 if (mCache == NULL) {
795 ALOGW("No memory trying to allocate decode cache table of %d bytes\n",
Ryan Mitchell2ad530d2018-03-29 15:49:10 -0700796 (int)(mHeader->stringCount*sizeof(char16_t**)));
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700797 return NULL;
798 }
799 }
800
Andreas Gampe2204f0b2014-10-21 23:04:54 -0700801 if (kDebugStringPoolNoisy) {
Ryan Mitchell2ad530d2018-03-29 15:49:10 -0700802 ALOGI("Caching UTF8 string: %s", u8str);
Andreas Gampe2204f0b2014-10-21 23:04:54 -0700803 }
Ryan Mitchell2ad530d2018-03-29 15:49:10 -0700804
Kenny Root19138462009-12-04 09:38:48 -0800805 mCache[idx] = u16str;
806 return u16str;
807 } else {
Steve Block8564c8d2012-01-05 23:22:43 +0000808 ALOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n",
Kenny Root300ba682010-11-09 14:37:23 -0800809 (long long)idx, (long long)(u8str+u8len-strings),
810 (long long)mStringPoolSize);
Kenny Root19138462009-12-04 09:38:48 -0800811 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 }
813 } else {
Steve Block8564c8d2012-01-05 23:22:43 +0000814 ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 (int)idx, (int)(off*sizeof(uint16_t)),
816 (int)(mStringPoolSize*sizeof(uint16_t)));
817 }
818 }
819 return NULL;
820}
821
Kenny Root780d2a12010-02-22 22:36:26 -0800822const char* ResStringPool::string8At(size_t idx, size_t* outLen) const
823{
824 if (mError == NO_ERROR && idx < mHeader->stringCount) {
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700825 if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) == 0) {
826 return NULL;
827 }
828 const uint32_t off = mEntries[idx]/sizeof(char);
Kenny Root780d2a12010-02-22 22:36:26 -0800829 if (off < (mStringPoolSize-1)) {
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700830 const uint8_t* strings = (uint8_t*)mStrings;
831 const uint8_t* str = strings+off;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700832
833 // Decode the UTF-16 length. This is not used if we're not
834 // converting to UTF-16 from UTF-8.
835 decodeLength(&str);
836
837 const size_t encLen = decodeLength(&str);
838 *outLen = encLen;
839
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700840 if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
Ryan Mitchell2ad530d2018-03-29 15:49:10 -0700841 return stringDecodeAt(idx, str, encLen, outLen);
842
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700843 } else {
844 ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
845 (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
Kenny Root780d2a12010-02-22 22:36:26 -0800846 }
847 } else {
Steve Block8564c8d2012-01-05 23:22:43 +0000848 ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n",
Kenny Root780d2a12010-02-22 22:36:26 -0800849 (int)idx, (int)(off*sizeof(uint16_t)),
850 (int)(mStringPoolSize*sizeof(uint16_t)));
851 }
852 }
853 return NULL;
854}
855
Ryan Mitchell2ad530d2018-03-29 15:49:10 -0700856/**
857 * AAPT incorrectly writes a truncated string length when the string size
858 * exceeded the maximum possible encode length value (0x7FFF). To decode a
859 * truncated length, iterate through length values that end in the encode length
860 * bits. Strings that exceed the maximum encode length are not placed into
861 * StringPools in AAPT2.
862 **/
863const char* ResStringPool::stringDecodeAt(size_t idx, const uint8_t* str,
864 const size_t encLen, size_t* outLen) const {
865 const uint8_t* strings = (uint8_t*)mStrings;
866
867 size_t i = 0, end = encLen;
868 while ((uint32_t)(str+end-strings) < mStringPoolSize) {
869 if (str[end] == 0x00) {
870 if (i != 0) {
871 ALOGW("Bad string block: string #%d is truncated (actual length is %d)",
872 (int)idx, (int)end);
873 }
874
875 *outLen = end;
876 return (const char*)str;
877 }
878
879 end = (++i << (sizeof(uint8_t) * 8 * 2 - 1)) | encLen;
880 }
881
882 // Reject malformed (non null-terminated) strings
883 ALOGW("Bad string block: string #%d is not null-terminated",
884 (int)idx);
885 return NULL;
886}
887
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800888const String8 ResStringPool::string8ObjectAt(size_t idx) const
889{
890 size_t len;
Adam Lesinski4b2d0f22014-08-14 17:58:37 -0700891 const char *str = string8At(idx, &len);
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800892 if (str != NULL) {
Adam Lesinski4b2d0f22014-08-14 17:58:37 -0700893 return String8(str, len);
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800894 }
Adam Lesinski4b2d0f22014-08-14 17:58:37 -0700895
896 const char16_t *str16 = stringAt(idx, &len);
897 if (str16 != NULL) {
898 return String8(str16, len);
899 }
900 return String8();
Dianne Hackborn6c997a92012-01-31 11:27:43 -0800901}
902
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const
904{
905 return styleAt(ref.index);
906}
907
908const ResStringPool_span* ResStringPool::styleAt(size_t idx) const
909{
910 if (mError == NO_ERROR && idx < mHeader->styleCount) {
911 const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t));
912 if (off < mStylePoolSize) {
913 return (const ResStringPool_span*)(mStyles+off);
914 } else {
Steve Block8564c8d2012-01-05 23:22:43 +0000915 ALOGW("Bad string block: style #%d entry is at %d, past end at %d\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 (int)idx, (int)(off*sizeof(uint32_t)),
917 (int)(mStylePoolSize*sizeof(uint32_t)));
918 }
919 }
920 return NULL;
921}
922
923ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
924{
925 if (mError != NO_ERROR) {
926 return mError;
927 }
928
929 size_t len;
930
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700931 if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -0700932 if (kDebugStringPoolNoisy) {
933 ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string());
934 }
Kenny Root19138462009-12-04 09:38:48 -0800935
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700936 // The string pool contains UTF 8 strings; we don't want to cause
937 // temporary UTF-16 strings to be created as we search.
938 if (mHeader->flags&ResStringPool_header::SORTED_FLAG) {
939 // Do a binary search for the string... this is a little tricky,
940 // because the strings are sorted with strzcmp16(). So to match
941 // the ordering, we need to convert strings in the pool to UTF-16.
942 // But we don't want to hit the cache, so instead we will have a
943 // local temporary allocation for the conversions.
Sergio Giro03b95c72016-07-21 14:44:07 +0100944 size_t convBufferLen = strLen + 4;
945 char16_t* convBuffer = (char16_t*)calloc(convBufferLen, sizeof(char16_t));
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700946 ssize_t l = 0;
947 ssize_t h = mHeader->stringCount-1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800948
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700949 ssize_t mid;
950 while (l <= h) {
951 mid = l + (h - l)/2;
952 const uint8_t* s = (const uint8_t*)string8At(mid, &len);
953 int c;
954 if (s != NULL) {
Sergio Giro03b95c72016-07-21 14:44:07 +0100955 char16_t* end = utf8_to_utf16(s, len, convBuffer, convBufferLen);
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700956 c = strzcmp16(convBuffer, end-convBuffer, str, strLen);
957 } else {
958 c = -1;
959 }
Andreas Gampe2204f0b2014-10-21 23:04:54 -0700960 if (kDebugStringPoolNoisy) {
961 ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
962 (const char*)s, c, (int)l, (int)mid, (int)h);
963 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700964 if (c == 0) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -0700965 if (kDebugStringPoolNoisy) {
966 ALOGI("MATCH!");
967 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700968 free(convBuffer);
969 return mid;
970 } else if (c < 0) {
971 l = mid + 1;
972 } else {
973 h = mid - 1;
974 }
975 }
976 free(convBuffer);
977 } else {
978 // It is unusual to get the ID from an unsorted string block...
979 // most often this happens because we want to get IDs for style
980 // span tags; since those always appear at the end of the string
981 // block, start searching at the back.
982 String8 str8(str, strLen);
983 const size_t str8Len = str8.size();
984 for (int i=mHeader->stringCount-1; i>=0; i--) {
985 const char* s = string8At(i, &len);
Andreas Gampe2204f0b2014-10-21 23:04:54 -0700986 if (kDebugStringPoolNoisy) {
987 ALOGI("Looking at %s, i=%d\n", String8(s).string(), i);
988 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700989 if (s && str8Len == len && memcmp(s, str8.string(), str8Len) == 0) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -0700990 if (kDebugStringPoolNoisy) {
991 ALOGI("MATCH!");
992 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700993 return i;
994 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 }
996 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -0700997
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 } else {
Andreas Gampe2204f0b2014-10-21 23:04:54 -0700999 if (kDebugStringPoolNoisy) {
1000 ALOGI("indexOfString UTF-16: %s", String8(str, strLen).string());
1001 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001002
1003 if (mHeader->flags&ResStringPool_header::SORTED_FLAG) {
1004 // Do a binary search for the string...
1005 ssize_t l = 0;
1006 ssize_t h = mHeader->stringCount-1;
1007
1008 ssize_t mid;
1009 while (l <= h) {
1010 mid = l + (h - l)/2;
1011 const char16_t* s = stringAt(mid, &len);
1012 int c = s ? strzcmp16(s, len, str, strLen) : -1;
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001013 if (kDebugStringPoolNoisy) {
1014 ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
1015 String8(s).string(), c, (int)l, (int)mid, (int)h);
1016 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001017 if (c == 0) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001018 if (kDebugStringPoolNoisy) {
1019 ALOGI("MATCH!");
1020 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001021 return mid;
1022 } else if (c < 0) {
1023 l = mid + 1;
1024 } else {
1025 h = mid - 1;
1026 }
1027 }
1028 } else {
1029 // It is unusual to get the ID from an unsorted string block...
1030 // most often this happens because we want to get IDs for style
1031 // span tags; since those always appear at the end of the string
1032 // block, start searching at the back.
1033 for (int i=mHeader->stringCount-1; i>=0; i--) {
1034 const char16_t* s = stringAt(i, &len);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001035 if (kDebugStringPoolNoisy) {
1036 ALOGI("Looking at %s, i=%d\n", String8(s).string(), i);
1037 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001038 if (s && strLen == len && strzcmp16(s, len, str, strLen) == 0) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001039 if (kDebugStringPoolNoisy) {
1040 ALOGI("MATCH!");
1041 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001042 return i;
1043 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 }
1045 }
1046 }
1047
1048 return NAME_NOT_FOUND;
1049}
1050
1051size_t ResStringPool::size() const
1052{
1053 return (mError == NO_ERROR) ? mHeader->stringCount : 0;
1054}
1055
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001056size_t ResStringPool::styleCount() const
1057{
1058 return (mError == NO_ERROR) ? mHeader->styleCount : 0;
1059}
1060
1061size_t ResStringPool::bytes() const
1062{
1063 return (mError == NO_ERROR) ? mHeader->header.size : 0;
1064}
1065
1066bool ResStringPool::isSorted() const
1067{
1068 return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0;
1069}
1070
Kenny Rootbb79f642009-12-10 14:20:15 -08001071bool ResStringPool::isUTF8() const
1072{
1073 return (mHeader->flags&ResStringPool_header::UTF8_FLAG)!=0;
1074}
Kenny Rootbb79f642009-12-10 14:20:15 -08001075
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001076// --------------------------------------------------------------------
1077// --------------------------------------------------------------------
1078// --------------------------------------------------------------------
1079
1080ResXMLParser::ResXMLParser(const ResXMLTree& tree)
1081 : mTree(tree), mEventCode(BAD_DOCUMENT)
1082{
1083}
1084
1085void ResXMLParser::restart()
1086{
1087 mCurNode = NULL;
1088 mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT;
1089}
Dianne Hackborncf244ad2010-03-09 15:00:30 -08001090const ResStringPool& ResXMLParser::getStrings() const
1091{
1092 return mTree.mStrings;
1093}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001094
1095ResXMLParser::event_code_t ResXMLParser::getEventType() const
1096{
1097 return mEventCode;
1098}
1099
1100ResXMLParser::event_code_t ResXMLParser::next()
1101{
1102 if (mEventCode == START_DOCUMENT) {
1103 mCurNode = mTree.mRootNode;
1104 mCurExt = mTree.mRootExt;
1105 return (mEventCode=mTree.mRootCode);
1106 } else if (mEventCode >= FIRST_CHUNK_CODE) {
1107 return nextNode();
1108 }
1109 return mEventCode;
1110}
1111
Mathias Agopian5f910972009-06-22 02:35:32 -07001112int32_t ResXMLParser::getCommentID() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113{
1114 return mCurNode != NULL ? dtohl(mCurNode->comment.index) : -1;
1115}
1116
Dan Albertf348c152014-09-08 18:28:00 -07001117const char16_t* ResXMLParser::getComment(size_t* outLen) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118{
1119 int32_t id = getCommentID();
1120 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1121}
1122
Mathias Agopian5f910972009-06-22 02:35:32 -07001123uint32_t ResXMLParser::getLineNumber() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124{
1125 return mCurNode != NULL ? dtohl(mCurNode->lineNumber) : -1;
1126}
1127
Mathias Agopian5f910972009-06-22 02:35:32 -07001128int32_t ResXMLParser::getTextID() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001129{
1130 if (mEventCode == TEXT) {
1131 return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index);
1132 }
1133 return -1;
1134}
1135
Dan Albertf348c152014-09-08 18:28:00 -07001136const char16_t* ResXMLParser::getText(size_t* outLen) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137{
1138 int32_t id = getTextID();
1139 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1140}
1141
1142ssize_t ResXMLParser::getTextValue(Res_value* outValue) const
1143{
1144 if (mEventCode == TEXT) {
1145 outValue->copyFrom_dtoh(((const ResXMLTree_cdataExt*)mCurExt)->typedData);
1146 return sizeof(Res_value);
1147 }
1148 return BAD_TYPE;
1149}
1150
Mathias Agopian5f910972009-06-22 02:35:32 -07001151int32_t ResXMLParser::getNamespacePrefixID() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001152{
1153 if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) {
1154 return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->prefix.index);
1155 }
1156 return -1;
1157}
1158
Dan Albertf348c152014-09-08 18:28:00 -07001159const char16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160{
1161 int32_t id = getNamespacePrefixID();
1162 //printf("prefix=%d event=%p\n", id, mEventCode);
1163 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1164}
1165
Mathias Agopian5f910972009-06-22 02:35:32 -07001166int32_t ResXMLParser::getNamespaceUriID() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167{
1168 if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) {
1169 return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->uri.index);
1170 }
1171 return -1;
1172}
1173
Dan Albertf348c152014-09-08 18:28:00 -07001174const char16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175{
1176 int32_t id = getNamespaceUriID();
1177 //printf("uri=%d event=%p\n", id, mEventCode);
1178 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1179}
1180
Mathias Agopian5f910972009-06-22 02:35:32 -07001181int32_t ResXMLParser::getElementNamespaceID() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182{
1183 if (mEventCode == START_TAG) {
1184 return dtohl(((const ResXMLTree_attrExt*)mCurExt)->ns.index);
1185 }
1186 if (mEventCode == END_TAG) {
1187 return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->ns.index);
1188 }
1189 return -1;
1190}
1191
Dan Albertf348c152014-09-08 18:28:00 -07001192const char16_t* ResXMLParser::getElementNamespace(size_t* outLen) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001193{
1194 int32_t id = getElementNamespaceID();
1195 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1196}
1197
Mathias Agopian5f910972009-06-22 02:35:32 -07001198int32_t ResXMLParser::getElementNameID() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001199{
1200 if (mEventCode == START_TAG) {
1201 return dtohl(((const ResXMLTree_attrExt*)mCurExt)->name.index);
1202 }
1203 if (mEventCode == END_TAG) {
1204 return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->name.index);
1205 }
1206 return -1;
1207}
1208
Dan Albertf348c152014-09-08 18:28:00 -07001209const char16_t* ResXMLParser::getElementName(size_t* outLen) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001210{
1211 int32_t id = getElementNameID();
1212 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1213}
1214
1215size_t ResXMLParser::getAttributeCount() const
1216{
1217 if (mEventCode == START_TAG) {
1218 return dtohs(((const ResXMLTree_attrExt*)mCurExt)->attributeCount);
1219 }
1220 return 0;
1221}
1222
Mathias Agopian5f910972009-06-22 02:35:32 -07001223int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001224{
1225 if (mEventCode == START_TAG) {
1226 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
1227 if (idx < dtohs(tag->attributeCount)) {
1228 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
1229 (((const uint8_t*)tag)
1230 + dtohs(tag->attributeStart)
1231 + (dtohs(tag->attributeSize)*idx));
1232 return dtohl(attr->ns.index);
1233 }
1234 }
1235 return -2;
1236}
1237
Dan Albertf348c152014-09-08 18:28:00 -07001238const char16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001239{
1240 int32_t id = getAttributeNamespaceID(idx);
1241 //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001242 if (kDebugXMLNoisy) {
1243 printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id);
1244 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1246}
1247
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001248const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) const
1249{
1250 int32_t id = getAttributeNamespaceID(idx);
1251 //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001252 if (kDebugXMLNoisy) {
1253 printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id);
1254 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001255 return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL;
1256}
1257
Mathias Agopian5f910972009-06-22 02:35:32 -07001258int32_t ResXMLParser::getAttributeNameID(size_t idx) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001259{
1260 if (mEventCode == START_TAG) {
1261 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
1262 if (idx < dtohs(tag->attributeCount)) {
1263 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
1264 (((const uint8_t*)tag)
1265 + dtohs(tag->attributeStart)
1266 + (dtohs(tag->attributeSize)*idx));
1267 return dtohl(attr->name.index);
1268 }
1269 }
1270 return -1;
1271}
1272
Dan Albertf348c152014-09-08 18:28:00 -07001273const char16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001274{
1275 int32_t id = getAttributeNameID(idx);
1276 //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001277 if (kDebugXMLNoisy) {
1278 printf("getAttributeName 0x%zx=0x%x\n", idx, id);
1279 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001280 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1281}
1282
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001283const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const
1284{
1285 int32_t id = getAttributeNameID(idx);
1286 //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001287 if (kDebugXMLNoisy) {
1288 printf("getAttributeName 0x%zx=0x%x\n", idx, id);
1289 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001290 return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL;
1291}
1292
Mathias Agopian5f910972009-06-22 02:35:32 -07001293uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001294{
1295 int32_t id = getAttributeNameID(idx);
1296 if (id >= 0 && (size_t)id < mTree.mNumResIds) {
Adam Lesinskia7d1d732014-10-01 18:24:54 -07001297 uint32_t resId = dtohl(mTree.mResIds[id]);
1298 if (mTree.mDynamicRefTable != NULL) {
1299 mTree.mDynamicRefTable->lookupResourceId(&resId);
1300 }
1301 return resId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001302 }
1303 return 0;
1304}
1305
Mathias Agopian5f910972009-06-22 02:35:32 -07001306int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307{
1308 if (mEventCode == START_TAG) {
1309 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
1310 if (idx < dtohs(tag->attributeCount)) {
1311 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
1312 (((const uint8_t*)tag)
1313 + dtohs(tag->attributeStart)
1314 + (dtohs(tag->attributeSize)*idx));
1315 return dtohl(attr->rawValue.index);
1316 }
1317 }
1318 return -1;
1319}
1320
Dan Albertf348c152014-09-08 18:28:00 -07001321const char16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322{
1323 int32_t id = getAttributeValueStringID(idx);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001324 if (kDebugXMLNoisy) {
1325 printf("getAttributeValue 0x%zx=0x%x\n", idx, id);
1326 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
1328}
1329
1330int32_t ResXMLParser::getAttributeDataType(size_t idx) const
1331{
1332 if (mEventCode == START_TAG) {
1333 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
1334 if (idx < dtohs(tag->attributeCount)) {
1335 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
1336 (((const uint8_t*)tag)
1337 + dtohs(tag->attributeStart)
1338 + (dtohs(tag->attributeSize)*idx));
Adam Lesinskide898ff2014-01-29 18:20:45 -08001339 uint8_t type = attr->typedValue.dataType;
1340 if (type != Res_value::TYPE_DYNAMIC_REFERENCE) {
1341 return type;
1342 }
1343
1344 // This is a dynamic reference. We adjust those references
1345 // to regular references at this level, so lie to the caller.
1346 return Res_value::TYPE_REFERENCE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001347 }
1348 }
1349 return Res_value::TYPE_NULL;
1350}
1351
1352int32_t ResXMLParser::getAttributeData(size_t idx) const
1353{
1354 if (mEventCode == START_TAG) {
1355 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
1356 if (idx < dtohs(tag->attributeCount)) {
1357 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
1358 (((const uint8_t*)tag)
1359 + dtohs(tag->attributeStart)
1360 + (dtohs(tag->attributeSize)*idx));
Adam Lesinskide898ff2014-01-29 18:20:45 -08001361 if (attr->typedValue.dataType != Res_value::TYPE_DYNAMIC_REFERENCE ||
1362 mTree.mDynamicRefTable == NULL) {
1363 return dtohl(attr->typedValue.data);
1364 }
1365
1366 uint32_t data = dtohl(attr->typedValue.data);
1367 if (mTree.mDynamicRefTable->lookupResourceId(&data) == NO_ERROR) {
1368 return data;
1369 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001370 }
1371 }
1372 return 0;
1373}
1374
1375ssize_t ResXMLParser::getAttributeValue(size_t idx, Res_value* outValue) const
1376{
1377 if (mEventCode == START_TAG) {
1378 const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt;
1379 if (idx < dtohs(tag->attributeCount)) {
1380 const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*)
1381 (((const uint8_t*)tag)
1382 + dtohs(tag->attributeStart)
1383 + (dtohs(tag->attributeSize)*idx));
1384 outValue->copyFrom_dtoh(attr->typedValue);
Adam Lesinskide898ff2014-01-29 18:20:45 -08001385 if (mTree.mDynamicRefTable != NULL &&
1386 mTree.mDynamicRefTable->lookupResourceValue(outValue) != NO_ERROR) {
1387 return BAD_TYPE;
1388 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001389 return sizeof(Res_value);
1390 }
1391 }
1392 return BAD_TYPE;
1393}
1394
1395ssize_t ResXMLParser::indexOfAttribute(const char* ns, const char* attr) const
1396{
1397 String16 nsStr(ns != NULL ? ns : "");
1398 String16 attrStr(attr);
1399 return indexOfAttribute(ns ? nsStr.string() : NULL, ns ? nsStr.size() : 0,
1400 attrStr.string(), attrStr.size());
1401}
1402
1403ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen,
1404 const char16_t* attr, size_t attrLen) const
1405{
1406 if (mEventCode == START_TAG) {
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001407 if (attr == NULL) {
1408 return NAME_NOT_FOUND;
1409 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 const size_t N = getAttributeCount();
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001411 if (mTree.mStrings.isUTF8()) {
1412 String8 ns8, attr8;
1413 if (ns != NULL) {
1414 ns8 = String8(ns, nsLen);
1415 }
1416 attr8 = String8(attr, attrLen);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001417 if (kDebugStringPoolNoisy) {
1418 ALOGI("indexOfAttribute UTF8 %s (%zu) / %s (%zu)", ns8.string(), nsLen,
1419 attr8.string(), attrLen);
1420 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001421 for (size_t i=0; i<N; i++) {
1422 size_t curNsLen = 0, curAttrLen = 0;
1423 const char* curNs = getAttributeNamespace8(i, &curNsLen);
1424 const char* curAttr = getAttributeName8(i, &curAttrLen);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001425 if (kDebugStringPoolNoisy) {
1426 ALOGI(" curNs=%s (%zu), curAttr=%s (%zu)", curNs, curNsLen, curAttr, curAttrLen);
1427 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001428 if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen
1429 && memcmp(attr8.string(), curAttr, attrLen) == 0) {
1430 if (ns == NULL) {
1431 if (curNs == NULL) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001432 if (kDebugStringPoolNoisy) {
1433 ALOGI(" FOUND!");
1434 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001435 return i;
1436 }
1437 } else if (curNs != NULL) {
1438 //printf(" --> ns=%s, curNs=%s\n",
1439 // String8(ns).string(), String8(curNs).string());
1440 if (memcmp(ns8.string(), curNs, nsLen) == 0) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001441 if (kDebugStringPoolNoisy) {
1442 ALOGI(" FOUND!");
1443 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001444 return i;
1445 }
1446 }
1447 }
1448 }
1449 } else {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001450 if (kDebugStringPoolNoisy) {
1451 ALOGI("indexOfAttribute UTF16 %s (%zu) / %s (%zu)",
1452 String8(ns, nsLen).string(), nsLen,
1453 String8(attr, attrLen).string(), attrLen);
1454 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001455 for (size_t i=0; i<N; i++) {
1456 size_t curNsLen = 0, curAttrLen = 0;
1457 const char16_t* curNs = getAttributeNamespace(i, &curNsLen);
1458 const char16_t* curAttr = getAttributeName(i, &curAttrLen);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001459 if (kDebugStringPoolNoisy) {
1460 ALOGI(" curNs=%s (%zu), curAttr=%s (%zu)",
1461 String8(curNs, curNsLen).string(), curNsLen,
1462 String8(curAttr, curAttrLen).string(), curAttrLen);
1463 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001464 if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen
1465 && (memcmp(attr, curAttr, attrLen*sizeof(char16_t)) == 0)) {
1466 if (ns == NULL) {
1467 if (curNs == NULL) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001468 if (kDebugStringPoolNoisy) {
1469 ALOGI(" FOUND!");
1470 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001471 return i;
1472 }
1473 } else if (curNs != NULL) {
1474 //printf(" --> ns=%s, curNs=%s\n",
1475 // String8(ns).string(), String8(curNs).string());
1476 if (memcmp(ns, curNs, nsLen*sizeof(char16_t)) == 0) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001477 if (kDebugStringPoolNoisy) {
1478 ALOGI(" FOUND!");
1479 }
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07001480 return i;
1481 }
1482 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483 }
1484 }
1485 }
1486 }
1487
1488 return NAME_NOT_FOUND;
1489}
1490
1491ssize_t ResXMLParser::indexOfID() const
1492{
1493 if (mEventCode == START_TAG) {
1494 const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->idIndex);
1495 if (idx > 0) return (idx-1);
1496 }
1497 return NAME_NOT_FOUND;
1498}
1499
1500ssize_t ResXMLParser::indexOfClass() const
1501{
1502 if (mEventCode == START_TAG) {
1503 const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->classIndex);
1504 if (idx > 0) return (idx-1);
1505 }
1506 return NAME_NOT_FOUND;
1507}
1508
1509ssize_t ResXMLParser::indexOfStyle() const
1510{
1511 if (mEventCode == START_TAG) {
1512 const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->styleIndex);
1513 if (idx > 0) return (idx-1);
1514 }
1515 return NAME_NOT_FOUND;
1516}
1517
1518ResXMLParser::event_code_t ResXMLParser::nextNode()
1519{
1520 if (mEventCode < 0) {
1521 return mEventCode;
1522 }
1523
1524 do {
1525 const ResXMLTree_node* next = (const ResXMLTree_node*)
1526 (((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size));
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001527 if (kDebugXMLNoisy) {
1528 ALOGI("Next node: prev=%p, next=%p\n", mCurNode, next);
1529 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07001530
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001531 if (((const uint8_t*)next) >= mTree.mDataEnd) {
1532 mCurNode = NULL;
1533 return (mEventCode=END_DOCUMENT);
1534 }
1535
1536 if (mTree.validateNode(next) != NO_ERROR) {
1537 mCurNode = NULL;
1538 return (mEventCode=BAD_DOCUMENT);
1539 }
1540
1541 mCurNode = next;
1542 const uint16_t headerSize = dtohs(next->header.headerSize);
1543 const uint32_t totalSize = dtohl(next->header.size);
1544 mCurExt = ((const uint8_t*)next) + headerSize;
1545 size_t minExtSize = 0;
1546 event_code_t eventCode = (event_code_t)dtohs(next->header.type);
1547 switch ((mEventCode=eventCode)) {
1548 case RES_XML_START_NAMESPACE_TYPE:
1549 case RES_XML_END_NAMESPACE_TYPE:
1550 minExtSize = sizeof(ResXMLTree_namespaceExt);
1551 break;
1552 case RES_XML_START_ELEMENT_TYPE:
1553 minExtSize = sizeof(ResXMLTree_attrExt);
1554 break;
1555 case RES_XML_END_ELEMENT_TYPE:
1556 minExtSize = sizeof(ResXMLTree_endElementExt);
1557 break;
1558 case RES_XML_CDATA_TYPE:
1559 minExtSize = sizeof(ResXMLTree_cdataExt);
1560 break;
1561 default:
Steve Block8564c8d2012-01-05 23:22:43 +00001562 ALOGW("Unknown XML block: header type %d in node at %d\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001563 (int)dtohs(next->header.type),
1564 (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)));
1565 continue;
1566 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07001567
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001568 if ((totalSize-headerSize) < minExtSize) {
Steve Block8564c8d2012-01-05 23:22:43 +00001569 ALOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001570 (int)dtohs(next->header.type),
1571 (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)),
1572 (int)(totalSize-headerSize), (int)minExtSize);
1573 return (mEventCode=BAD_DOCUMENT);
1574 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07001575
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001576 //printf("CurNode=%p, CurExt=%p, headerSize=%d, minExtSize=%d\n",
1577 // mCurNode, mCurExt, headerSize, minExtSize);
Mark Salyzyn00adb862014-03-19 11:00:06 -07001578
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001579 return eventCode;
1580 } while (true);
1581}
1582
1583void ResXMLParser::getPosition(ResXMLParser::ResXMLPosition* pos) const
1584{
1585 pos->eventCode = mEventCode;
1586 pos->curNode = mCurNode;
1587 pos->curExt = mCurExt;
1588}
1589
1590void ResXMLParser::setPosition(const ResXMLParser::ResXMLPosition& pos)
1591{
1592 mEventCode = pos.eventCode;
1593 mCurNode = pos.curNode;
1594 mCurExt = pos.curExt;
1595}
1596
Aurimas Liutikas949b05d2019-01-30 17:20:41 -08001597void ResXMLParser::setSourceResourceId(const uint32_t resId)
1598{
1599 mSourceResourceId = resId;
1600}
1601
1602uint32_t ResXMLParser::getSourceResourceId() const
1603{
1604 return mSourceResourceId;
1605}
1606
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001607// --------------------------------------------------------------------
1608
1609static volatile int32_t gCount = 0;
1610
Adam Lesinskide898ff2014-01-29 18:20:45 -08001611ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001612 : ResXMLParser(*this)
Ryan Mitchella41e66a2018-05-15 15:08:58 -07001613 , mDynamicRefTable((dynamicRefTable != nullptr) ? dynamicRefTable->clone()
1614 : std::unique_ptr<DynamicRefTable>(nullptr))
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001615 , mError(NO_INIT), mOwnedData(NULL)
1616{
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001617 if (kDebugResXMLTree) {
1618 ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1);
1619 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001620 restart();
1621}
1622
Adam Lesinskide898ff2014-01-29 18:20:45 -08001623ResXMLTree::ResXMLTree()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 : ResXMLParser(*this)
Ryan Mitchella41e66a2018-05-15 15:08:58 -07001625 , mDynamicRefTable(std::unique_ptr<DynamicRefTable>(nullptr))
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001626 , mError(NO_INIT), mOwnedData(NULL)
1627{
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001628 if (kDebugResXMLTree) {
1629 ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1);
1630 }
Adam Lesinskide898ff2014-01-29 18:20:45 -08001631 restart();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001632}
1633
1634ResXMLTree::~ResXMLTree()
1635{
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001636 if (kDebugResXMLTree) {
1637 ALOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1);
1638 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001639 uninit();
1640}
1641
1642status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData)
1643{
1644 uninit();
1645 mEventCode = START_DOCUMENT;
1646
Kenny Root32d6aef2012-10-10 10:23:47 -07001647 if (!data || !size) {
1648 return (mError=BAD_TYPE);
1649 }
1650
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001651 if (copyData) {
1652 mOwnedData = malloc(size);
1653 if (mOwnedData == NULL) {
1654 return (mError=NO_MEMORY);
1655 }
1656 memcpy(mOwnedData, data, size);
1657 data = mOwnedData;
1658 }
1659
1660 mHeader = (const ResXMLTree_header*)data;
1661 mSize = dtohl(mHeader->header.size);
1662 if (dtohs(mHeader->header.headerSize) > mSize || mSize > size) {
Steve Block8564c8d2012-01-05 23:22:43 +00001663 ALOGW("Bad XML block: header size %d or total size %d is larger than data size %d\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001664 (int)dtohs(mHeader->header.headerSize),
1665 (int)dtohl(mHeader->header.size), (int)size);
1666 mError = BAD_TYPE;
1667 restart();
1668 return mError;
1669 }
1670 mDataEnd = ((const uint8_t*)mHeader) + mSize;
1671
1672 mStrings.uninit();
1673 mRootNode = NULL;
1674 mResIds = NULL;
1675 mNumResIds = 0;
1676
1677 // First look for a couple interesting chunks: the string block
1678 // and first XML node.
1679 const ResChunk_header* chunk =
1680 (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize));
1681 const ResChunk_header* lastChunk = chunk;
1682 while (((const uint8_t*)chunk) < (mDataEnd-sizeof(ResChunk_header)) &&
1683 ((const uint8_t*)chunk) < (mDataEnd-dtohl(chunk->size))) {
1684 status_t err = validate_chunk(chunk, sizeof(ResChunk_header), mDataEnd, "XML");
1685 if (err != NO_ERROR) {
1686 mError = err;
1687 goto done;
1688 }
1689 const uint16_t type = dtohs(chunk->type);
1690 const size_t size = dtohl(chunk->size);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001691 if (kDebugXMLNoisy) {
1692 printf("Scanning @ %p: type=0x%x, size=0x%zx\n",
1693 (void*)(((uintptr_t)chunk)-((uintptr_t)mHeader)), type, size);
1694 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001695 if (type == RES_STRING_POOL_TYPE) {
1696 mStrings.setTo(chunk, size);
1697 } else if (type == RES_XML_RESOURCE_MAP_TYPE) {
1698 mResIds = (const uint32_t*)
1699 (((const uint8_t*)chunk)+dtohs(chunk->headerSize));
1700 mNumResIds = (dtohl(chunk->size)-dtohs(chunk->headerSize))/sizeof(uint32_t);
1701 } else if (type >= RES_XML_FIRST_CHUNK_TYPE
1702 && type <= RES_XML_LAST_CHUNK_TYPE) {
1703 if (validateNode((const ResXMLTree_node*)chunk) != NO_ERROR) {
1704 mError = BAD_TYPE;
1705 goto done;
1706 }
1707 mCurNode = (const ResXMLTree_node*)lastChunk;
1708 if (nextNode() == BAD_DOCUMENT) {
1709 mError = BAD_TYPE;
1710 goto done;
1711 }
1712 mRootNode = mCurNode;
1713 mRootExt = mCurExt;
1714 mRootCode = mEventCode;
1715 break;
1716 } else {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07001717 if (kDebugXMLNoisy) {
1718 printf("Skipping unknown chunk!\n");
1719 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001720 }
1721 lastChunk = chunk;
1722 chunk = (const ResChunk_header*)
1723 (((const uint8_t*)chunk) + size);
1724 }
1725
1726 if (mRootNode == NULL) {
Steve Block8564c8d2012-01-05 23:22:43 +00001727 ALOGW("Bad XML block: no root element node found\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001728 mError = BAD_TYPE;
1729 goto done;
1730 }
1731
1732 mError = mStrings.getError();
1733
1734done:
1735 restart();
1736 return mError;
1737}
1738
1739status_t ResXMLTree::getError() const
1740{
1741 return mError;
1742}
1743
1744void ResXMLTree::uninit()
1745{
1746 mError = NO_INIT;
Kenny Root19138462009-12-04 09:38:48 -08001747 mStrings.uninit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001748 if (mOwnedData) {
1749 free(mOwnedData);
1750 mOwnedData = NULL;
1751 }
1752 restart();
1753}
1754
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001755status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const
1756{
1757 const uint16_t eventCode = dtohs(node->header.type);
1758
1759 status_t err = validate_chunk(
1760 &node->header, sizeof(ResXMLTree_node),
1761 mDataEnd, "ResXMLTree_node");
1762
1763 if (err >= NO_ERROR) {
1764 // Only perform additional validation on START nodes
1765 if (eventCode != RES_XML_START_ELEMENT_TYPE) {
1766 return NO_ERROR;
1767 }
1768
1769 const uint16_t headerSize = dtohs(node->header.headerSize);
1770 const uint32_t size = dtohl(node->header.size);
1771 const ResXMLTree_attrExt* attrExt = (const ResXMLTree_attrExt*)
1772 (((const uint8_t*)node) + headerSize);
1773 // check for sensical values pulled out of the stream so far...
1774 if ((size >= headerSize + sizeof(ResXMLTree_attrExt))
1775 && ((void*)attrExt > (void*)node)) {
1776 const size_t attrSize = ((size_t)dtohs(attrExt->attributeSize))
1777 * dtohs(attrExt->attributeCount);
1778 if ((dtohs(attrExt->attributeStart)+attrSize) <= (size-headerSize)) {
1779 return NO_ERROR;
1780 }
Steve Block8564c8d2012-01-05 23:22:43 +00001781 ALOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782 (unsigned int)(dtohs(attrExt->attributeStart)+attrSize),
1783 (unsigned int)(size-headerSize));
1784 }
1785 else {
Steve Block8564c8d2012-01-05 23:22:43 +00001786 ALOGW("Bad XML start block: node header size 0x%x, size 0x%x\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001787 (unsigned int)headerSize, (unsigned int)size);
1788 }
1789 return BAD_TYPE;
1790 }
1791
1792 return err;
1793
1794#if 0
1795 const bool isStart = dtohs(node->header.type) == RES_XML_START_ELEMENT_TYPE;
1796
1797 const uint16_t headerSize = dtohs(node->header.headerSize);
1798 const uint32_t size = dtohl(node->header.size);
1799
1800 if (headerSize >= (isStart ? sizeof(ResXMLTree_attrNode) : sizeof(ResXMLTree_node))) {
1801 if (size >= headerSize) {
1802 if (((const uint8_t*)node) <= (mDataEnd-size)) {
1803 if (!isStart) {
1804 return NO_ERROR;
1805 }
1806 if ((((size_t)dtohs(node->attributeSize))*dtohs(node->attributeCount))
1807 <= (size-headerSize)) {
1808 return NO_ERROR;
1809 }
Steve Block8564c8d2012-01-05 23:22:43 +00001810 ALOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001811 ((int)dtohs(node->attributeSize))*dtohs(node->attributeCount),
1812 (int)(size-headerSize));
1813 return BAD_TYPE;
1814 }
Steve Block8564c8d2012-01-05 23:22:43 +00001815 ALOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001816 (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), (int)mSize);
1817 return BAD_TYPE;
1818 }
Steve Block8564c8d2012-01-05 23:22:43 +00001819 ALOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)),
1821 (int)headerSize, (int)size);
1822 return BAD_TYPE;
1823 }
Steve Block8564c8d2012-01-05 23:22:43 +00001824 ALOGW("Bad XML block: node at 0x%x header size 0x%x too small\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001825 (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)),
1826 (int)headerSize);
1827 return BAD_TYPE;
1828#endif
1829}
1830
1831// --------------------------------------------------------------------
1832// --------------------------------------------------------------------
1833// --------------------------------------------------------------------
1834
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001835void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) {
1836 const size_t size = dtohl(o.size);
1837 if (size >= sizeof(ResTable_config)) {
1838 *this = o;
1839 } else {
1840 memcpy(this, &o, size);
1841 memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size);
1842 }
1843}
1844
Narayan Kamath48620f12014-01-20 13:57:11 +00001845/* static */ size_t unpackLanguageOrRegion(const char in[2], const char base,
1846 char out[4]) {
1847 if (in[0] & 0x80) {
1848 // The high bit is "1", which means this is a packed three letter
1849 // language code.
1850
1851 // The smallest 5 bits of the second char are the first alphabet.
1852 const uint8_t first = in[1] & 0x1f;
1853 // The last three bits of the second char and the first two bits
1854 // of the first char are the second alphabet.
1855 const uint8_t second = ((in[1] & 0xe0) >> 5) + ((in[0] & 0x03) << 3);
1856 // Bits 3 to 7 (inclusive) of the first char are the third alphabet.
1857 const uint8_t third = (in[0] & 0x7c) >> 2;
1858
1859 out[0] = first + base;
1860 out[1] = second + base;
1861 out[2] = third + base;
1862 out[3] = 0;
1863
1864 return 3;
1865 }
1866
1867 if (in[0]) {
1868 memcpy(out, in, 2);
1869 memset(out + 2, 0, 2);
1870 return 2;
1871 }
1872
1873 memset(out, 0, 4);
1874 return 0;
1875}
1876
Narayan Kamath788fa412014-01-21 15:32:36 +00001877/* static */ void packLanguageOrRegion(const char* in, const char base,
Narayan Kamath48620f12014-01-20 13:57:11 +00001878 char out[2]) {
Narayan Kamath788fa412014-01-21 15:32:36 +00001879 if (in[2] == 0 || in[2] == '-') {
Narayan Kamath48620f12014-01-20 13:57:11 +00001880 out[0] = in[0];
1881 out[1] = in[1];
1882 } else {
Narayan Kamathb2975912014-06-30 15:59:39 +01001883 uint8_t first = (in[0] - base) & 0x007f;
1884 uint8_t second = (in[1] - base) & 0x007f;
1885 uint8_t third = (in[2] - base) & 0x007f;
Narayan Kamath48620f12014-01-20 13:57:11 +00001886
1887 out[0] = (0x80 | (third << 2) | (second >> 3));
1888 out[1] = ((second << 5) | first);
1889 }
1890}
1891
1892
Narayan Kamath788fa412014-01-21 15:32:36 +00001893void ResTable_config::packLanguage(const char* language) {
Narayan Kamath48620f12014-01-20 13:57:11 +00001894 packLanguageOrRegion(language, 'a', this->language);
1895}
1896
Narayan Kamath788fa412014-01-21 15:32:36 +00001897void ResTable_config::packRegion(const char* region) {
Narayan Kamath48620f12014-01-20 13:57:11 +00001898 packLanguageOrRegion(region, '0', this->country);
1899}
1900
1901size_t ResTable_config::unpackLanguage(char language[4]) const {
1902 return unpackLanguageOrRegion(this->language, 'a', language);
1903}
1904
1905size_t ResTable_config::unpackRegion(char region[4]) const {
1906 return unpackLanguageOrRegion(this->country, '0', region);
1907}
1908
1909
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001910void ResTable_config::copyFromDtoH(const ResTable_config& o) {
1911 copyFromDeviceNoSwap(o);
1912 size = sizeof(ResTable_config);
1913 mcc = dtohs(mcc);
1914 mnc = dtohs(mnc);
1915 density = dtohs(density);
1916 screenWidth = dtohs(screenWidth);
1917 screenHeight = dtohs(screenHeight);
1918 sdkVersion = dtohs(sdkVersion);
1919 minorVersion = dtohs(minorVersion);
1920 smallestScreenWidthDp = dtohs(smallestScreenWidthDp);
1921 screenWidthDp = dtohs(screenWidthDp);
1922 screenHeightDp = dtohs(screenHeightDp);
1923}
1924
1925void ResTable_config::swapHtoD() {
1926 size = htodl(size);
1927 mcc = htods(mcc);
1928 mnc = htods(mnc);
1929 density = htods(density);
1930 screenWidth = htods(screenWidth);
1931 screenHeight = htods(screenHeight);
1932 sdkVersion = htods(sdkVersion);
1933 minorVersion = htods(minorVersion);
1934 smallestScreenWidthDp = htods(smallestScreenWidthDp);
1935 screenWidthDp = htods(screenWidthDp);
1936 screenHeightDp = htods(screenHeightDp);
1937}
1938
Narayan Kamath48620f12014-01-20 13:57:11 +00001939/* static */ inline int compareLocales(const ResTable_config &l, const ResTable_config &r) {
1940 if (l.locale != r.locale) {
Ivan Lozano599fed42017-11-01 11:05:45 -07001941 return (l.locale > r.locale) ? 1 : -1;
Narayan Kamath48620f12014-01-20 13:57:11 +00001942 }
1943
Igor Viarheichyk7ec28a82017-11-10 11:58:38 -08001944 // The language & region are equal, so compare the scripts, variants and
1945 // numbering systms in this order. Comparison of variants and numbering
1946 // systems should happen very infrequently (if at all.)
1947 // The comparison code relies on memcmp low-level optimizations that make it
1948 // more efficient than strncmp.
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08001949 const char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'};
Roozbeh Pournader79608982016-03-03 15:06:46 -08001950 const char *lScript = l.localeScriptWasComputed ? emptyScript : l.localeScript;
1951 const char *rScript = r.localeScriptWasComputed ? emptyScript : r.localeScript;
Igor Viarheichyk7ec28a82017-11-10 11:58:38 -08001952
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08001953 int script = memcmp(lScript, rScript, sizeof(l.localeScript));
Narayan Kamath48620f12014-01-20 13:57:11 +00001954 if (script) {
1955 return script;
1956 }
1957
Igor Viarheichyk7ec28a82017-11-10 11:58:38 -08001958 int variant = memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant));
1959 if (variant) {
1960 return variant;
1961 }
1962
1963 return memcmp(l.localeNumberingSystem, r.localeNumberingSystem,
1964 sizeof(l.localeNumberingSystem));
Narayan Kamath48620f12014-01-20 13:57:11 +00001965}
1966
Dianne Hackborn6c997a92012-01-31 11:27:43 -08001967int ResTable_config::compare(const ResTable_config& o) const {
Ivan Lozano599fed42017-11-01 11:05:45 -07001968 if (imsi != o.imsi) {
1969 return (imsi > o.imsi) ? 1 : -1;
1970 }
1971
1972 int32_t diff = compareLocales(*this, o);
1973 if (diff < 0) {
1974 return -1;
1975 }
1976 if (diff > 0) {
1977 return 1;
1978 }
1979
1980 if (screenType != o.screenType) {
1981 return (screenType > o.screenType) ? 1 : -1;
1982 }
1983 if (input != o.input) {
1984 return (input > o.input) ? 1 : -1;
1985 }
1986 if (screenSize != o.screenSize) {
1987 return (screenSize > o.screenSize) ? 1 : -1;
1988 }
1989 if (version != o.version) {
1990 return (version > o.version) ? 1 : -1;
1991 }
1992 if (screenLayout != o.screenLayout) {
1993 return (screenLayout > o.screenLayout) ? 1 : -1;
1994 }
1995 if (screenLayout2 != o.screenLayout2) {
1996 return (screenLayout2 > o.screenLayout2) ? 1 : -1;
1997 }
1998 if (colorMode != o.colorMode) {
1999 return (colorMode > o.colorMode) ? 1 : -1;
2000 }
2001 if (uiMode != o.uiMode) {
2002 return (uiMode > o.uiMode) ? 1 : -1;
2003 }
2004 if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
2005 return (smallestScreenWidthDp > o.smallestScreenWidthDp) ? 1 : -1;
2006 }
2007 if (screenSizeDp != o.screenSizeDp) {
2008 return (screenSizeDp > o.screenSizeDp) ? 1 : -1;
2009 }
2010 return 0;
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002011}
2012
2013int ResTable_config::compareLogical(const ResTable_config& o) const {
2014 if (mcc != o.mcc) {
2015 return mcc < o.mcc ? -1 : 1;
2016 }
2017 if (mnc != o.mnc) {
2018 return mnc < o.mnc ? -1 : 1;
2019 }
Narayan Kamath48620f12014-01-20 13:57:11 +00002020
2021 int diff = compareLocales(*this, o);
2022 if (diff < 0) {
2023 return -1;
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002024 }
Narayan Kamath48620f12014-01-20 13:57:11 +00002025 if (diff > 0) {
2026 return 1;
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002027 }
Narayan Kamath48620f12014-01-20 13:57:11 +00002028
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07002029 if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) {
2030 return (screenLayout & MASK_LAYOUTDIR) < (o.screenLayout & MASK_LAYOUTDIR) ? -1 : 1;
2031 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002032 if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
2033 return smallestScreenWidthDp < o.smallestScreenWidthDp ? -1 : 1;
2034 }
2035 if (screenWidthDp != o.screenWidthDp) {
2036 return screenWidthDp < o.screenWidthDp ? -1 : 1;
2037 }
2038 if (screenHeightDp != o.screenHeightDp) {
2039 return screenHeightDp < o.screenHeightDp ? -1 : 1;
2040 }
2041 if (screenWidth != o.screenWidth) {
2042 return screenWidth < o.screenWidth ? -1 : 1;
2043 }
2044 if (screenHeight != o.screenHeight) {
2045 return screenHeight < o.screenHeight ? -1 : 1;
2046 }
2047 if (density != o.density) {
2048 return density < o.density ? -1 : 1;
2049 }
2050 if (orientation != o.orientation) {
2051 return orientation < o.orientation ? -1 : 1;
2052 }
2053 if (touchscreen != o.touchscreen) {
2054 return touchscreen < o.touchscreen ? -1 : 1;
2055 }
2056 if (input != o.input) {
2057 return input < o.input ? -1 : 1;
2058 }
2059 if (screenLayout != o.screenLayout) {
2060 return screenLayout < o.screenLayout ? -1 : 1;
2061 }
Adam Lesinski2738c962015-05-14 14:25:36 -07002062 if (screenLayout2 != o.screenLayout2) {
2063 return screenLayout2 < o.screenLayout2 ? -1 : 1;
2064 }
Romain Guy48327452017-01-23 17:03:35 -08002065 if (colorMode != o.colorMode) {
2066 return colorMode < o.colorMode ? -1 : 1;
Romain Guyc9ba5592017-01-18 16:34:42 -08002067 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002068 if (uiMode != o.uiMode) {
2069 return uiMode < o.uiMode ? -1 : 1;
2070 }
2071 if (version != o.version) {
2072 return version < o.version ? -1 : 1;
2073 }
2074 return 0;
2075}
2076
2077int ResTable_config::diff(const ResTable_config& o) const {
2078 int diffs = 0;
2079 if (mcc != o.mcc) diffs |= CONFIG_MCC;
2080 if (mnc != o.mnc) diffs |= CONFIG_MNC;
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002081 if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION;
2082 if (density != o.density) diffs |= CONFIG_DENSITY;
2083 if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN;
2084 if (((inputFlags^o.inputFlags)&(MASK_KEYSHIDDEN|MASK_NAVHIDDEN)) != 0)
2085 diffs |= CONFIG_KEYBOARD_HIDDEN;
2086 if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD;
2087 if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION;
2088 if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE;
2089 if (version != o.version) diffs |= CONFIG_VERSION;
Fabrice Di Meglio35099352012-12-12 11:52:03 -08002090 if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) diffs |= CONFIG_LAYOUTDIR;
2091 if ((screenLayout & ~MASK_LAYOUTDIR) != (o.screenLayout & ~MASK_LAYOUTDIR)) diffs |= CONFIG_SCREEN_LAYOUT;
Adam Lesinski2738c962015-05-14 14:25:36 -07002092 if ((screenLayout2 & MASK_SCREENROUND) != (o.screenLayout2 & MASK_SCREENROUND)) diffs |= CONFIG_SCREEN_ROUND;
Romain Guy48327452017-01-23 17:03:35 -08002093 if ((colorMode & MASK_WIDE_COLOR_GAMUT) != (o.colorMode & MASK_WIDE_COLOR_GAMUT)) diffs |= CONFIG_COLOR_MODE;
2094 if ((colorMode & MASK_HDR) != (o.colorMode & MASK_HDR)) diffs |= CONFIG_COLOR_MODE;
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002095 if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE;
2096 if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE;
2097 if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE;
Narayan Kamath48620f12014-01-20 13:57:11 +00002098
2099 const int diff = compareLocales(*this, o);
2100 if (diff) diffs |= CONFIG_LOCALE;
2101
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002102 return diffs;
2103}
2104
Igor Viarheichyk7ec28a82017-11-10 11:58:38 -08002105// There isn't a well specified "importance" order between variants and
2106// scripts. We can't easily tell whether, say "en-Latn-US" is more or less
2107// specific than "en-US-POSIX".
2108//
2109// We therefore arbitrarily decide to give priority to variants over
2110// scripts since it seems more useful to do so. We will consider
2111// "en-US-POSIX" to be more specific than "en-Latn-US".
2112//
2113// Unicode extension keywords are considered to be less important than
2114// scripts and variants.
2115inline int ResTable_config::getImportanceScoreOfLocale() const {
2116 return (localeVariant[0] ? 4 : 0)
2117 + (localeScript[0] && !localeScriptWasComputed ? 2: 0)
2118 + (localeNumberingSystem[0] ? 1: 0);
2119}
2120
Narayan Kamath48620f12014-01-20 13:57:11 +00002121int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const {
2122 if (locale || o.locale) {
2123 if (language[0] != o.language[0]) {
2124 if (!language[0]) return -1;
2125 if (!o.language[0]) return 1;
2126 }
2127
2128 if (country[0] != o.country[0]) {
2129 if (!country[0]) return -1;
2130 if (!o.country[0]) return 1;
2131 }
2132 }
2133
Igor Viarheichyk7ec28a82017-11-10 11:58:38 -08002134 return getImportanceScoreOfLocale() - o.getImportanceScoreOfLocale();
Narayan Kamath48620f12014-01-20 13:57:11 +00002135}
2136
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002137bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const {
2138 // The order of the following tests defines the importance of one
2139 // configuration parameter over another. Those tests first are more
2140 // important, trumping any values in those following them.
2141 if (imsi || o.imsi) {
2142 if (mcc != o.mcc) {
2143 if (!mcc) return false;
2144 if (!o.mcc) return true;
2145 }
2146
2147 if (mnc != o.mnc) {
2148 if (!mnc) return false;
2149 if (!o.mnc) return true;
2150 }
2151 }
2152
2153 if (locale || o.locale) {
Narayan Kamath48620f12014-01-20 13:57:11 +00002154 const int diff = isLocaleMoreSpecificThan(o);
2155 if (diff < 0) {
2156 return false;
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002157 }
2158
Narayan Kamath48620f12014-01-20 13:57:11 +00002159 if (diff > 0) {
2160 return true;
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002161 }
2162 }
2163
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07002164 if (screenLayout || o.screenLayout) {
2165 if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0) {
2166 if (!(screenLayout & MASK_LAYOUTDIR)) return false;
2167 if (!(o.screenLayout & MASK_LAYOUTDIR)) return true;
2168 }
2169 }
2170
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002171 if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
2172 if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
2173 if (!smallestScreenWidthDp) return false;
2174 if (!o.smallestScreenWidthDp) return true;
2175 }
2176 }
2177
2178 if (screenSizeDp || o.screenSizeDp) {
2179 if (screenWidthDp != o.screenWidthDp) {
2180 if (!screenWidthDp) return false;
2181 if (!o.screenWidthDp) return true;
2182 }
2183
2184 if (screenHeightDp != o.screenHeightDp) {
2185 if (!screenHeightDp) return false;
2186 if (!o.screenHeightDp) return true;
2187 }
2188 }
2189
2190 if (screenLayout || o.screenLayout) {
2191 if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) {
2192 if (!(screenLayout & MASK_SCREENSIZE)) return false;
2193 if (!(o.screenLayout & MASK_SCREENSIZE)) return true;
2194 }
2195 if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0) {
2196 if (!(screenLayout & MASK_SCREENLONG)) return false;
2197 if (!(o.screenLayout & MASK_SCREENLONG)) return true;
2198 }
2199 }
2200
Adam Lesinski2738c962015-05-14 14:25:36 -07002201 if (screenLayout2 || o.screenLayout2) {
2202 if (((screenLayout2^o.screenLayout2) & MASK_SCREENROUND) != 0) {
2203 if (!(screenLayout2 & MASK_SCREENROUND)) return false;
2204 if (!(o.screenLayout2 & MASK_SCREENROUND)) return true;
2205 }
2206 }
2207
Romain Guy48327452017-01-23 17:03:35 -08002208 if (colorMode || o.colorMode) {
2209 if (((colorMode^o.colorMode) & MASK_HDR) != 0) {
2210 if (!(colorMode & MASK_HDR)) return false;
2211 if (!(o.colorMode & MASK_HDR)) return true;
Romain Guyc9ba5592017-01-18 16:34:42 -08002212 }
Romain Guy48327452017-01-23 17:03:35 -08002213 if (((colorMode^o.colorMode) & MASK_WIDE_COLOR_GAMUT) != 0) {
2214 if (!(colorMode & MASK_WIDE_COLOR_GAMUT)) return false;
2215 if (!(o.colorMode & MASK_WIDE_COLOR_GAMUT)) return true;
Romain Guyc9ba5592017-01-18 16:34:42 -08002216 }
2217 }
2218
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002219 if (orientation != o.orientation) {
2220 if (!orientation) return false;
2221 if (!o.orientation) return true;
2222 }
2223
2224 if (uiMode || o.uiMode) {
2225 if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0) {
2226 if (!(uiMode & MASK_UI_MODE_TYPE)) return false;
2227 if (!(o.uiMode & MASK_UI_MODE_TYPE)) return true;
2228 }
2229 if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0) {
2230 if (!(uiMode & MASK_UI_MODE_NIGHT)) return false;
2231 if (!(o.uiMode & MASK_UI_MODE_NIGHT)) return true;
2232 }
2233 }
2234
2235 // density is never 'more specific'
2236 // as the default just equals 160
2237
2238 if (touchscreen != o.touchscreen) {
2239 if (!touchscreen) return false;
2240 if (!o.touchscreen) return true;
2241 }
2242
2243 if (input || o.input) {
2244 if (((inputFlags^o.inputFlags) & MASK_KEYSHIDDEN) != 0) {
2245 if (!(inputFlags & MASK_KEYSHIDDEN)) return false;
2246 if (!(o.inputFlags & MASK_KEYSHIDDEN)) return true;
2247 }
2248
2249 if (((inputFlags^o.inputFlags) & MASK_NAVHIDDEN) != 0) {
2250 if (!(inputFlags & MASK_NAVHIDDEN)) return false;
2251 if (!(o.inputFlags & MASK_NAVHIDDEN)) return true;
2252 }
2253
2254 if (keyboard != o.keyboard) {
2255 if (!keyboard) return false;
2256 if (!o.keyboard) return true;
2257 }
2258
2259 if (navigation != o.navigation) {
2260 if (!navigation) return false;
2261 if (!o.navigation) return true;
2262 }
2263 }
2264
2265 if (screenSize || o.screenSize) {
2266 if (screenWidth != o.screenWidth) {
2267 if (!screenWidth) return false;
2268 if (!o.screenWidth) return true;
2269 }
2270
2271 if (screenHeight != o.screenHeight) {
2272 if (!screenHeight) return false;
2273 if (!o.screenHeight) return true;
2274 }
2275 }
2276
2277 if (version || o.version) {
2278 if (sdkVersion != o.sdkVersion) {
2279 if (!sdkVersion) return false;
2280 if (!o.sdkVersion) return true;
2281 }
2282
2283 if (minorVersion != o.minorVersion) {
2284 if (!minorVersion) return false;
2285 if (!o.minorVersion) return true;
2286 }
2287 }
2288 return false;
2289}
2290
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002291// Codes for specially handled languages and regions
2292static const char kEnglish[2] = {'e', 'n'}; // packed version of "en"
2293static const char kUnitedStates[2] = {'U', 'S'}; // packed version of "US"
2294static const char kFilipino[2] = {'\xAD', '\x05'}; // packed version of "fil"
2295static const char kTagalog[2] = {'t', 'l'}; // packed version of "tl"
2296
2297// Checks if two language or region codes are identical
2298inline bool areIdentical(const char code1[2], const char code2[2]) {
2299 return code1[0] == code2[0] && code1[1] == code2[1];
2300}
2301
2302inline bool langsAreEquivalent(const char lang1[2], const char lang2[2]) {
2303 return areIdentical(lang1, lang2) ||
2304 (areIdentical(lang1, kTagalog) && areIdentical(lang2, kFilipino)) ||
2305 (areIdentical(lang1, kFilipino) && areIdentical(lang2, kTagalog));
2306}
2307
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002308bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
2309 const ResTable_config* requested) const {
2310 if (requested->locale == 0) {
2311 // The request doesn't have a locale, so no resource is better
2312 // than the other.
2313 return false;
2314 }
2315
2316 if (locale == 0 && o.locale == 0) {
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002317 // The locale part of both resources is empty, so none is better
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002318 // than the other.
2319 return false;
2320 }
2321
2322 // Non-matching locales have been filtered out, so both resources
2323 // match the requested locale.
2324 //
2325 // Because of the locale-related checks in match() and the checks, we know
2326 // that:
2327 // 1) The resource languages are either empty or match the request;
2328 // and
2329 // 2) If the request's script is known, the resource scripts are either
2330 // unknown or match the request.
2331
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002332 if (!langsAreEquivalent(language, o.language)) {
2333 // The languages of the two resources are not equivalent. If we are
2334 // here, we can only assume that the two resources matched the request
2335 // because one doesn't have a language and the other has a matching
2336 // language.
Roozbeh Pournader27953c32016-02-01 13:49:52 -08002337 //
2338 // We consider the one that has the language specified a better match.
2339 //
2340 // The exception is that we consider no-language resources a better match
2341 // for US English and similar locales than locales that are a descendant
2342 // of Internatinal English (en-001), since no-language resources are
2343 // where the US English resource have traditionally lived for most apps.
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002344 if (areIdentical(requested->language, kEnglish)) {
2345 if (areIdentical(requested->country, kUnitedStates)) {
Roozbeh Pournader27953c32016-02-01 13:49:52 -08002346 // For US English itself, we consider a no-locale resource a
2347 // better match if the other resource has a country other than
2348 // US specified.
2349 if (language[0] != '\0') {
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002350 return country[0] == '\0' || areIdentical(country, kUnitedStates);
Roozbeh Pournader27953c32016-02-01 13:49:52 -08002351 } else {
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002352 return !(o.country[0] == '\0' || areIdentical(o.country, kUnitedStates));
Roozbeh Pournader27953c32016-02-01 13:49:52 -08002353 }
2354 } else if (localeDataIsCloseToUsEnglish(requested->country)) {
2355 if (language[0] != '\0') {
2356 return localeDataIsCloseToUsEnglish(country);
2357 } else {
2358 return !localeDataIsCloseToUsEnglish(o.country);
2359 }
2360 }
2361 }
2362 return (language[0] != '\0');
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002363 }
2364
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002365 // If we are here, both the resources have an equivalent non-empty language
2366 // to the request.
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002367 //
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002368 // Because the languages are equivalent, computeScript() always returns a
2369 // non-empty script for languages it knows about, and we have passed the
2370 // script checks in match(), the scripts are either all unknown or are all
2371 // the same. So we can't gain anything by checking the scripts. We need to
2372 // check the region and variant.
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002373
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002374 // See if any of the regions is better than the other.
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002375 const int region_comparison = localeDataCompareRegions(
2376 country, o.country,
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002377 requested->language, requested->localeScript, requested->country);
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002378 if (region_comparison != 0) {
2379 return (region_comparison > 0);
2380 }
2381
2382 // The regions are the same. Try the variant.
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002383 const bool localeMatches = strncmp(
2384 localeVariant, requested->localeVariant, sizeof(localeVariant)) == 0;
2385 const bool otherMatches = strncmp(
2386 o.localeVariant, requested->localeVariant, sizeof(localeVariant)) == 0;
2387 if (localeMatches != otherMatches) {
2388 return localeMatches;
2389 }
2390
Igor Viarheichyk7ec28a82017-11-10 11:58:38 -08002391 // The variants are the same, try numbering system.
2392 const bool localeNumsysMatches = strncmp(localeNumberingSystem,
2393 requested->localeNumberingSystem,
2394 sizeof(localeNumberingSystem)) == 0;
2395 const bool otherNumsysMatches = strncmp(o.localeNumberingSystem,
2396 requested->localeNumberingSystem,
2397 sizeof(localeNumberingSystem)) == 0;
2398 if (localeNumsysMatches != otherNumsysMatches) {
2399 return localeNumsysMatches;
2400 }
2401
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002402 // Finally, the languages, although equivalent, may still be different
2403 // (like for Tagalog and Filipino). Identical is better than just
2404 // equivalent.
2405 if (areIdentical(language, requested->language)
2406 && !areIdentical(o.language, requested->language)) {
2407 return true;
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002408 }
2409
2410 return false;
2411}
2412
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002413bool ResTable_config::isBetterThan(const ResTable_config& o,
2414 const ResTable_config* requested) const {
2415 if (requested) {
2416 if (imsi || o.imsi) {
2417 if ((mcc != o.mcc) && requested->mcc) {
2418 return (mcc);
2419 }
2420
2421 if ((mnc != o.mnc) && requested->mnc) {
2422 return (mnc);
2423 }
2424 }
2425
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002426 if (isLocaleBetterThan(o, requested)) {
2427 return true;
Narayan Kamath48620f12014-01-20 13:57:11 +00002428 }
2429
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07002430 if (screenLayout || o.screenLayout) {
2431 if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0
2432 && (requested->screenLayout & MASK_LAYOUTDIR)) {
2433 int myLayoutDir = screenLayout & MASK_LAYOUTDIR;
2434 int oLayoutDir = o.screenLayout & MASK_LAYOUTDIR;
2435 return (myLayoutDir > oLayoutDir);
2436 }
2437 }
2438
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002439 if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
2440 // The configuration closest to the actual size is best.
2441 // We assume that larger configs have already been filtered
2442 // out at this point. That means we just want the largest one.
Dianne Hackborn5c6dfeb2012-03-09 13:17:17 -08002443 if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
2444 return smallestScreenWidthDp > o.smallestScreenWidthDp;
2445 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002446 }
2447
2448 if (screenSizeDp || o.screenSizeDp) {
2449 // "Better" is based on the sum of the difference between both
2450 // width and height from the requested dimensions. We are
2451 // assuming the invalid configs (with smaller dimens) have
2452 // already been filtered. Note that if a particular dimension
2453 // is unspecified, we will end up with a large value (the
2454 // difference between 0 and the requested dimension), which is
2455 // good since we will prefer a config that has specified a
2456 // dimension value.
2457 int myDelta = 0, otherDelta = 0;
2458 if (requested->screenWidthDp) {
2459 myDelta += requested->screenWidthDp - screenWidthDp;
2460 otherDelta += requested->screenWidthDp - o.screenWidthDp;
2461 }
2462 if (requested->screenHeightDp) {
2463 myDelta += requested->screenHeightDp - screenHeightDp;
2464 otherDelta += requested->screenHeightDp - o.screenHeightDp;
2465 }
Andreas Gampe2204f0b2014-10-21 23:04:54 -07002466 if (kDebugTableSuperNoisy) {
2467 ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d",
2468 screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp,
2469 requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta);
2470 }
Dianne Hackborn5c6dfeb2012-03-09 13:17:17 -08002471 if (myDelta != otherDelta) {
2472 return myDelta < otherDelta;
2473 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002474 }
2475
2476 if (screenLayout || o.screenLayout) {
2477 if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0
2478 && (requested->screenLayout & MASK_SCREENSIZE)) {
2479 // A little backwards compatibility here: undefined is
2480 // considered equivalent to normal. But only if the
2481 // requested size is at least normal; otherwise, small
2482 // is better than the default.
2483 int mySL = (screenLayout & MASK_SCREENSIZE);
2484 int oSL = (o.screenLayout & MASK_SCREENSIZE);
2485 int fixedMySL = mySL;
2486 int fixedOSL = oSL;
2487 if ((requested->screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) {
2488 if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL;
2489 if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL;
2490 }
2491 // For screen size, the best match is the one that is
2492 // closest to the requested screen size, but not over
2493 // (the not over part is dealt with in match() below).
2494 if (fixedMySL == fixedOSL) {
2495 // If the two are the same, but 'this' is actually
2496 // undefined, then the other is really a better match.
2497 if (mySL == 0) return false;
2498 return true;
2499 }
Dianne Hackborn5c6dfeb2012-03-09 13:17:17 -08002500 if (fixedMySL != fixedOSL) {
2501 return fixedMySL > fixedOSL;
2502 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002503 }
2504 if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0
2505 && (requested->screenLayout & MASK_SCREENLONG)) {
2506 return (screenLayout & MASK_SCREENLONG);
2507 }
2508 }
2509
Adam Lesinski2738c962015-05-14 14:25:36 -07002510 if (screenLayout2 || o.screenLayout2) {
2511 if (((screenLayout2^o.screenLayout2) & MASK_SCREENROUND) != 0 &&
2512 (requested->screenLayout2 & MASK_SCREENROUND)) {
2513 return screenLayout2 & MASK_SCREENROUND;
2514 }
2515 }
2516
Romain Guy48327452017-01-23 17:03:35 -08002517 if (colorMode || o.colorMode) {
2518 if (((colorMode^o.colorMode) & MASK_WIDE_COLOR_GAMUT) != 0 &&
2519 (requested->colorMode & MASK_WIDE_COLOR_GAMUT)) {
2520 return colorMode & MASK_WIDE_COLOR_GAMUT;
Romain Guyc9ba5592017-01-18 16:34:42 -08002521 }
Romain Guy48327452017-01-23 17:03:35 -08002522 if (((colorMode^o.colorMode) & MASK_HDR) != 0 &&
2523 (requested->colorMode & MASK_HDR)) {
2524 return colorMode & MASK_HDR;
Romain Guyc9ba5592017-01-18 16:34:42 -08002525 }
2526 }
2527
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002528 if ((orientation != o.orientation) && requested->orientation) {
2529 return (orientation);
2530 }
2531
2532 if (uiMode || o.uiMode) {
2533 if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0
2534 && (requested->uiMode & MASK_UI_MODE_TYPE)) {
2535 return (uiMode & MASK_UI_MODE_TYPE);
2536 }
2537 if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0
2538 && (requested->uiMode & MASK_UI_MODE_NIGHT)) {
2539 return (uiMode & MASK_UI_MODE_NIGHT);
2540 }
2541 }
2542
2543 if (screenType || o.screenType) {
2544 if (density != o.density) {
Adam Lesinski31245b42014-08-22 19:10:56 -07002545 // Use the system default density (DENSITY_MEDIUM, 160dpi) if none specified.
2546 const int thisDensity = density ? density : int(ResTable_config::DENSITY_MEDIUM);
2547 const int otherDensity = o.density ? o.density : int(ResTable_config::DENSITY_MEDIUM);
2548
2549 // We always prefer DENSITY_ANY over scaling a density bucket.
2550 if (thisDensity == ResTable_config::DENSITY_ANY) {
2551 return true;
2552 } else if (otherDensity == ResTable_config::DENSITY_ANY) {
2553 return false;
2554 }
2555
2556 int requestedDensity = requested->density;
2557 if (requested->density == 0 ||
2558 requested->density == ResTable_config::DENSITY_ANY) {
2559 requestedDensity = ResTable_config::DENSITY_MEDIUM;
2560 }
2561
2562 // DENSITY_ANY is now dealt with. We should look to
2563 // pick a density bucket and potentially scale it.
2564 // Any density is potentially useful
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002565 // because the system will scale it. Scaling down
2566 // is generally better than scaling up.
Adam Lesinski31245b42014-08-22 19:10:56 -07002567 int h = thisDensity;
2568 int l = otherDensity;
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002569 bool bImBigger = true;
2570 if (l > h) {
2571 int t = h;
2572 h = l;
2573 l = t;
2574 bImBigger = false;
2575 }
2576
Adam Lesinski31245b42014-08-22 19:10:56 -07002577 if (requestedDensity >= h) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002578 // requested value higher than both l and h, give h
2579 return bImBigger;
2580 }
Adam Lesinski31245b42014-08-22 19:10:56 -07002581 if (l >= requestedDensity) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002582 // requested value lower than both l and h, give l
2583 return !bImBigger;
2584 }
2585 // saying that scaling down is 2x better than up
Adam Lesinski31245b42014-08-22 19:10:56 -07002586 if (((2 * l) - requestedDensity) * h > requestedDensity * requestedDensity) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002587 return !bImBigger;
2588 } else {
2589 return bImBigger;
2590 }
2591 }
2592
2593 if ((touchscreen != o.touchscreen) && requested->touchscreen) {
2594 return (touchscreen);
2595 }
2596 }
2597
2598 if (input || o.input) {
2599 const int keysHidden = inputFlags & MASK_KEYSHIDDEN;
2600 const int oKeysHidden = o.inputFlags & MASK_KEYSHIDDEN;
2601 if (keysHidden != oKeysHidden) {
2602 const int reqKeysHidden =
2603 requested->inputFlags & MASK_KEYSHIDDEN;
2604 if (reqKeysHidden) {
2605
2606 if (!keysHidden) return false;
2607 if (!oKeysHidden) return true;
2608 // For compatibility, we count KEYSHIDDEN_NO as being
2609 // the same as KEYSHIDDEN_SOFT. Here we disambiguate
2610 // these by making an exact match more specific.
2611 if (reqKeysHidden == keysHidden) return true;
2612 if (reqKeysHidden == oKeysHidden) return false;
2613 }
2614 }
2615
2616 const int navHidden = inputFlags & MASK_NAVHIDDEN;
2617 const int oNavHidden = o.inputFlags & MASK_NAVHIDDEN;
2618 if (navHidden != oNavHidden) {
2619 const int reqNavHidden =
2620 requested->inputFlags & MASK_NAVHIDDEN;
2621 if (reqNavHidden) {
2622
2623 if (!navHidden) return false;
2624 if (!oNavHidden) return true;
2625 }
2626 }
2627
2628 if ((keyboard != o.keyboard) && requested->keyboard) {
2629 return (keyboard);
2630 }
2631
2632 if ((navigation != o.navigation) && requested->navigation) {
2633 return (navigation);
2634 }
2635 }
2636
2637 if (screenSize || o.screenSize) {
2638 // "Better" is based on the sum of the difference between both
2639 // width and height from the requested dimensions. We are
2640 // assuming the invalid configs (with smaller sizes) have
2641 // already been filtered. Note that if a particular dimension
2642 // is unspecified, we will end up with a large value (the
2643 // difference between 0 and the requested dimension), which is
2644 // good since we will prefer a config that has specified a
2645 // size value.
2646 int myDelta = 0, otherDelta = 0;
2647 if (requested->screenWidth) {
2648 myDelta += requested->screenWidth - screenWidth;
2649 otherDelta += requested->screenWidth - o.screenWidth;
2650 }
2651 if (requested->screenHeight) {
2652 myDelta += requested->screenHeight - screenHeight;
2653 otherDelta += requested->screenHeight - o.screenHeight;
2654 }
Dianne Hackborn5c6dfeb2012-03-09 13:17:17 -08002655 if (myDelta != otherDelta) {
2656 return myDelta < otherDelta;
2657 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002658 }
2659
2660 if (version || o.version) {
2661 if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) {
2662 return (sdkVersion > o.sdkVersion);
2663 }
2664
2665 if ((minorVersion != o.minorVersion) &&
2666 requested->minorVersion) {
2667 return (minorVersion);
2668 }
2669 }
2670
2671 return false;
2672 }
2673 return isMoreSpecificThan(o);
2674}
2675
2676bool ResTable_config::match(const ResTable_config& settings) const {
2677 if (imsi != 0) {
2678 if (mcc != 0 && mcc != settings.mcc) {
2679 return false;
2680 }
2681 if (mnc != 0 && mnc != settings.mnc) {
2682 return false;
2683 }
2684 }
2685 if (locale != 0) {
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002686 // Don't consider country and variants when deciding matches.
2687 // (Theoretically, the variant can also affect the script. For
2688 // example, "ar-alalc97" probably implies the Latin script, but since
2689 // CLDR doesn't support getting likely scripts for that, we'll assume
2690 // the variant doesn't change the script.)
Narayan Kamath48620f12014-01-20 13:57:11 +00002691 //
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002692 // If two configs differ only in their country and variant,
2693 // they can be weeded out in the isMoreSpecificThan test.
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002694 if (!langsAreEquivalent(language, settings.language)) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002695 return false;
2696 }
Narayan Kamath48620f12014-01-20 13:57:11 +00002697
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002698 // For backward compatibility and supporting private-use locales, we
2699 // fall back to old behavior if we couldn't determine the script for
Roozbeh Pournader4de45962016-02-11 17:58:24 -08002700 // either of the desired locale or the provided locale. But if we could determine
2701 // the scripts, they should be the same for the locales to match.
2702 bool countriesMustMatch = false;
2703 char computed_script[4];
2704 const char* script;
2705 if (settings.localeScript[0] == '\0') { // could not determine the request's script
2706 countriesMustMatch = true;
2707 } else {
Roozbeh Pournader79608982016-03-03 15:06:46 -08002708 if (localeScript[0] == '\0' && !localeScriptWasComputed) {
2709 // script was not provided or computed, so we try to compute it
Roozbeh Pournader4de45962016-02-11 17:58:24 -08002710 localeDataComputeScript(computed_script, language, country);
2711 if (computed_script[0] == '\0') { // we could not compute the script
2712 countriesMustMatch = true;
2713 } else {
2714 script = computed_script;
2715 }
2716 } else { // script was provided, so just use it
2717 script = localeScript;
2718 }
2719 }
2720
2721 if (countriesMustMatch) {
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002722 if (country[0] != '\0' && !areIdentical(country, settings.country)) {
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002723 return false;
2724 }
2725 } else {
Roozbeh Pournader4de45962016-02-11 17:58:24 -08002726 if (memcmp(script, settings.localeScript, sizeof(settings.localeScript)) != 0) {
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002727 return false;
2728 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002729 }
2730 }
Narayan Kamath48620f12014-01-20 13:57:11 +00002731
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002732 if (screenConfig != 0) {
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07002733 const int layoutDir = screenLayout&MASK_LAYOUTDIR;
2734 const int setLayoutDir = settings.screenLayout&MASK_LAYOUTDIR;
2735 if (layoutDir != 0 && layoutDir != setLayoutDir) {
2736 return false;
2737 }
2738
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002739 const int screenSize = screenLayout&MASK_SCREENSIZE;
2740 const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE;
2741 // Any screen sizes for larger screens than the setting do not
2742 // match.
2743 if (screenSize != 0 && screenSize > setScreenSize) {
2744 return false;
2745 }
2746
2747 const int screenLong = screenLayout&MASK_SCREENLONG;
2748 const int setScreenLong = settings.screenLayout&MASK_SCREENLONG;
2749 if (screenLong != 0 && screenLong != setScreenLong) {
2750 return false;
2751 }
2752
2753 const int uiModeType = uiMode&MASK_UI_MODE_TYPE;
2754 const int setUiModeType = settings.uiMode&MASK_UI_MODE_TYPE;
2755 if (uiModeType != 0 && uiModeType != setUiModeType) {
2756 return false;
2757 }
2758
2759 const int uiModeNight = uiMode&MASK_UI_MODE_NIGHT;
2760 const int setUiModeNight = settings.uiMode&MASK_UI_MODE_NIGHT;
2761 if (uiModeNight != 0 && uiModeNight != setUiModeNight) {
2762 return false;
2763 }
2764
2765 if (smallestScreenWidthDp != 0
2766 && smallestScreenWidthDp > settings.smallestScreenWidthDp) {
2767 return false;
2768 }
2769 }
Adam Lesinski2738c962015-05-14 14:25:36 -07002770
2771 if (screenConfig2 != 0) {
2772 const int screenRound = screenLayout2 & MASK_SCREENROUND;
2773 const int setScreenRound = settings.screenLayout2 & MASK_SCREENROUND;
2774 if (screenRound != 0 && screenRound != setScreenRound) {
2775 return false;
2776 }
Romain Guyc9ba5592017-01-18 16:34:42 -08002777
Romain Guy48327452017-01-23 17:03:35 -08002778 const int hdr = colorMode & MASK_HDR;
2779 const int setHdr = settings.colorMode & MASK_HDR;
Romain Guyc9ba5592017-01-18 16:34:42 -08002780 if (hdr != 0 && hdr != setHdr) {
2781 return false;
2782 }
2783
Romain Guy48327452017-01-23 17:03:35 -08002784 const int wideColorGamut = colorMode & MASK_WIDE_COLOR_GAMUT;
2785 const int setWideColorGamut = settings.colorMode & MASK_WIDE_COLOR_GAMUT;
Romain Guyc9ba5592017-01-18 16:34:42 -08002786 if (wideColorGamut != 0 && wideColorGamut != setWideColorGamut) {
2787 return false;
2788 }
Adam Lesinski2738c962015-05-14 14:25:36 -07002789 }
2790
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002791 if (screenSizeDp != 0) {
2792 if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07002793 if (kDebugTableSuperNoisy) {
2794 ALOGI("Filtering out width %d in requested %d", screenWidthDp,
2795 settings.screenWidthDp);
2796 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002797 return false;
2798 }
2799 if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07002800 if (kDebugTableSuperNoisy) {
2801 ALOGI("Filtering out height %d in requested %d", screenHeightDp,
2802 settings.screenHeightDp);
2803 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002804 return false;
2805 }
2806 }
2807 if (screenType != 0) {
2808 if (orientation != 0 && orientation != settings.orientation) {
2809 return false;
2810 }
2811 // density always matches - we can scale it. See isBetterThan
2812 if (touchscreen != 0 && touchscreen != settings.touchscreen) {
2813 return false;
2814 }
2815 }
2816 if (input != 0) {
2817 const int keysHidden = inputFlags&MASK_KEYSHIDDEN;
2818 const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN;
2819 if (keysHidden != 0 && keysHidden != setKeysHidden) {
2820 // For compatibility, we count a request for KEYSHIDDEN_NO as also
2821 // matching the more recent KEYSHIDDEN_SOFT. Basically
2822 // KEYSHIDDEN_NO means there is some kind of keyboard available.
Andreas Gampe2204f0b2014-10-21 23:04:54 -07002823 if (kDebugTableSuperNoisy) {
2824 ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden);
2825 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002826 if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07002827 if (kDebugTableSuperNoisy) {
2828 ALOGI("No match!");
2829 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002830 return false;
2831 }
2832 }
2833 const int navHidden = inputFlags&MASK_NAVHIDDEN;
2834 const int setNavHidden = settings.inputFlags&MASK_NAVHIDDEN;
2835 if (navHidden != 0 && navHidden != setNavHidden) {
2836 return false;
2837 }
2838 if (keyboard != 0 && keyboard != settings.keyboard) {
2839 return false;
2840 }
2841 if (navigation != 0 && navigation != settings.navigation) {
2842 return false;
2843 }
2844 }
2845 if (screenSize != 0) {
2846 if (screenWidth != 0 && screenWidth > settings.screenWidth) {
2847 return false;
2848 }
2849 if (screenHeight != 0 && screenHeight > settings.screenHeight) {
2850 return false;
2851 }
2852 }
2853 if (version != 0) {
2854 if (sdkVersion != 0 && sdkVersion > settings.sdkVersion) {
2855 return false;
2856 }
2857 if (minorVersion != 0 && minorVersion != settings.minorVersion) {
2858 return false;
2859 }
2860 }
2861 return true;
2862}
2863
Adam Lesinski8a9355a2015-03-10 16:55:43 -07002864void ResTable_config::appendDirLocale(String8& out) const {
2865 if (!language[0]) {
2866 return;
2867 }
Roozbeh Pournader79608982016-03-03 15:06:46 -08002868 const bool scriptWasProvided = localeScript[0] != '\0' && !localeScriptWasComputed;
Igor Viarheichyk7ec28a82017-11-10 11:58:38 -08002869 if (!scriptWasProvided && !localeVariant[0] && !localeNumberingSystem[0]) {
Adam Lesinski8a9355a2015-03-10 16:55:43 -07002870 // Legacy format.
2871 if (out.size() > 0) {
2872 out.append("-");
2873 }
2874
2875 char buf[4];
2876 size_t len = unpackLanguage(buf);
2877 out.append(buf, len);
2878
2879 if (country[0]) {
2880 out.append("-r");
2881 len = unpackRegion(buf);
2882 out.append(buf, len);
2883 }
2884 return;
2885 }
2886
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002887 // We are writing the modified BCP 47 tag.
Adam Lesinski8a9355a2015-03-10 16:55:43 -07002888 // It starts with 'b+' and uses '+' as a separator.
2889
2890 if (out.size() > 0) {
2891 out.append("-");
2892 }
2893 out.append("b+");
2894
2895 char buf[4];
2896 size_t len = unpackLanguage(buf);
2897 out.append(buf, len);
2898
Roozbeh Pournader79608982016-03-03 15:06:46 -08002899 if (scriptWasProvided) {
Adam Lesinski8a9355a2015-03-10 16:55:43 -07002900 out.append("+");
2901 out.append(localeScript, sizeof(localeScript));
2902 }
2903
2904 if (country[0]) {
2905 out.append("+");
2906 len = unpackRegion(buf);
2907 out.append(buf, len);
2908 }
2909
2910 if (localeVariant[0]) {
2911 out.append("+");
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08002912 out.append(localeVariant, strnlen(localeVariant, sizeof(localeVariant)));
Adam Lesinski8a9355a2015-03-10 16:55:43 -07002913 }
Igor Viarheichyk7ec28a82017-11-10 11:58:38 -08002914
2915 if (localeNumberingSystem[0]) {
2916 out.append("+u+nu+");
2917 out.append(localeNumberingSystem,
2918 strnlen(localeNumberingSystem, sizeof(localeNumberingSystem)));
2919 }
Adam Lesinski8a9355a2015-03-10 16:55:43 -07002920}
2921
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002922void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool canonicalize) const {
Narayan Kamath48620f12014-01-20 13:57:11 +00002923 memset(str, 0, RESTABLE_MAX_LOCALE_LEN);
2924
2925 // This represents the "any" locale value, which has traditionally been
2926 // represented by the empty string.
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002927 if (language[0] == '\0' && country[0] == '\0') {
Narayan Kamath48620f12014-01-20 13:57:11 +00002928 return;
2929 }
2930
2931 size_t charsWritten = 0;
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002932 if (language[0] != '\0') {
2933 if (canonicalize && areIdentical(language, kTagalog)) {
2934 // Replace Tagalog with Filipino if we are canonicalizing
2935 str[0] = 'f'; str[1] = 'i'; str[2] = 'l'; str[3] = '\0'; // 3-letter code for Filipino
2936 charsWritten += 3;
2937 } else {
2938 charsWritten += unpackLanguage(str);
2939 }
Narayan Kamath48620f12014-01-20 13:57:11 +00002940 }
2941
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002942 if (localeScript[0] != '\0' && !localeScriptWasComputed) {
2943 if (charsWritten > 0) {
Narayan Kamath788fa412014-01-21 15:32:36 +00002944 str[charsWritten++] = '-';
Narayan Kamath48620f12014-01-20 13:57:11 +00002945 }
2946 memcpy(str + charsWritten, localeScript, sizeof(localeScript));
Narayan Kamath788fa412014-01-21 15:32:36 +00002947 charsWritten += sizeof(localeScript);
2948 }
2949
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002950 if (country[0] != '\0') {
2951 if (charsWritten > 0) {
Narayan Kamath788fa412014-01-21 15:32:36 +00002952 str[charsWritten++] = '-';
2953 }
2954 charsWritten += unpackRegion(str + charsWritten);
Narayan Kamath48620f12014-01-20 13:57:11 +00002955 }
2956
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07002957 if (localeVariant[0] != '\0') {
2958 if (charsWritten > 0) {
Narayan Kamath788fa412014-01-21 15:32:36 +00002959 str[charsWritten++] = '-';
Narayan Kamath48620f12014-01-20 13:57:11 +00002960 }
2961 memcpy(str + charsWritten, localeVariant, sizeof(localeVariant));
Igor Viarheichyk7ec28a82017-11-10 11:58:38 -08002962 charsWritten += strnlen(str + charsWritten, sizeof(localeVariant));
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002963 }
Igor Viarheichyke7bc60a2017-10-20 15:09:13 -07002964
Igor Viarheichyk7ec28a82017-11-10 11:58:38 -08002965 // Add Unicode extension only if at least one other locale component is present
2966 if (localeNumberingSystem[0] != '\0' && charsWritten > 0) {
2967 static constexpr char NU_PREFIX[] = "-u-nu-";
2968 static constexpr size_t NU_PREFIX_LEN = sizeof(NU_PREFIX) - 1;
2969 memcpy(str + charsWritten, NU_PREFIX, NU_PREFIX_LEN);
2970 charsWritten += NU_PREFIX_LEN;
2971 memcpy(str + charsWritten, localeNumberingSystem, sizeof(localeNumberingSystem));
2972 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08002973}
2974
Igor Viarheichyke7bc60a2017-10-20 15:09:13 -07002975struct LocaleParserState {
2976 enum State : uint8_t {
2977 BASE, UNICODE_EXTENSION, IGNORE_THE_REST
2978 } parserState;
2979 enum UnicodeState : uint8_t {
2980 /* Initial state after the Unicode singleton is detected. Either a keyword
2981 * or an attribute is expected. */
2982 NO_KEY,
2983 /* Unicode extension key (but not attribute) is expected. Next states:
2984 * NO_KEY, IGNORE_KEY or NUMBERING_SYSTEM. */
2985 EXPECT_KEY,
2986 /* A key is detected, however it is not supported for now. Ignore its
2987 * value. Next states: IGNORE_KEY or NUMBERING_SYSTEM. */
2988 IGNORE_KEY,
2989 /* Numbering system key was detected. Store its value in the configuration
2990 * localeNumberingSystem field. Next state: EXPECT_KEY */
2991 NUMBERING_SYSTEM
2992 } unicodeState;
2993
2994 LocaleParserState(): parserState(BASE), unicodeState(NO_KEY) {}
2995};
2996
2997/* static */ inline LocaleParserState assignLocaleComponent(ResTable_config* config,
2998 const char* start, size_t size, LocaleParserState state) {
2999
3000 /* It is assumed that this function is not invoked with state.parserState
3001 * set to IGNORE_THE_REST. The condition is checked by setBcp47Locale
3002 * function. */
3003
3004 if (state.parserState == LocaleParserState::UNICODE_EXTENSION) {
3005 switch (size) {
3006 case 1:
3007 /* Other BCP 47 extensions are not supported at the moment */
3008 state.parserState = LocaleParserState::IGNORE_THE_REST;
3009 break;
3010 case 2:
3011 if (state.unicodeState == LocaleParserState::NO_KEY ||
3012 state.unicodeState == LocaleParserState::EXPECT_KEY) {
3013 /* Analyze Unicode extension key. Currently only 'nu'
3014 * (numbering system) is supported.*/
3015 if ((start[0] == 'n' || start[0] == 'N') &&
3016 (start[1] == 'u' || start[1] == 'U')) {
3017 state.unicodeState = LocaleParserState::NUMBERING_SYSTEM;
3018 } else {
3019 state.unicodeState = LocaleParserState::IGNORE_KEY;
3020 }
3021 } else {
3022 /* Keys are not allowed in other state allowed, ignore the rest. */
3023 state.parserState = LocaleParserState::IGNORE_THE_REST;
3024 }
3025 break;
3026 case 3:
3027 case 4:
3028 case 5:
3029 case 6:
3030 case 7:
3031 case 8:
3032 switch (state.unicodeState) {
3033 case LocaleParserState::NUMBERING_SYSTEM:
3034 /* Accept only the first occurrence of the numbering system. */
3035 if (config->localeNumberingSystem[0] == '\0') {
3036 for (size_t i = 0; i < size; ++i) {
3037 config->localeNumberingSystem[i] = tolower(start[i]);
3038 }
3039 state.unicodeState = LocaleParserState::EXPECT_KEY;
3040 } else {
3041 state.parserState = LocaleParserState::IGNORE_THE_REST;
3042 }
3043 break;
3044 case LocaleParserState::IGNORE_KEY:
3045 /* Unsupported Unicode keyword. Ignore. */
3046 state.unicodeState = LocaleParserState::EXPECT_KEY;
3047 break;
3048 case LocaleParserState::EXPECT_KEY:
3049 /* A keyword followed by an attribute is not allowed. */
3050 state.parserState = LocaleParserState::IGNORE_THE_REST;
3051 break;
3052 case LocaleParserState::NO_KEY:
3053 /* Extension attribute. Do nothing. */
3054 break;
3055 default:
3056 break;
3057 }
3058 break;
3059 default:
3060 /* Unexpected field length - ignore the rest and treat as an error */
3061 state.parserState = LocaleParserState::IGNORE_THE_REST;
3062 }
3063 return state;
3064 }
Narayan Kamath788fa412014-01-21 15:32:36 +00003065
3066 switch (size) {
3067 case 0:
Igor Viarheichyke7bc60a2017-10-20 15:09:13 -07003068 state.parserState = LocaleParserState::IGNORE_THE_REST;
3069 break;
3070 case 1:
3071 state.parserState = (start[0] == 'u' || start[0] == 'U')
3072 ? LocaleParserState::UNICODE_EXTENSION
3073 : LocaleParserState::IGNORE_THE_REST;
3074 break;
Narayan Kamath788fa412014-01-21 15:32:36 +00003075 case 2:
3076 case 3:
3077 config->language[0] ? config->packRegion(start) : config->packLanguage(start);
3078 break;
3079 case 4:
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08003080 if ('0' <= start[0] && start[0] <= '9') {
3081 // this is a variant, so fall through
3082 } else {
3083 config->localeScript[0] = toupper(start[0]);
3084 for (size_t i = 1; i < 4; ++i) {
3085 config->localeScript[i] = tolower(start[i]);
3086 }
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08003087 break;
Narayan Kamath788fa412014-01-21 15:32:36 +00003088 }
Mårten Kongstade0930d32018-10-18 14:50:15 +02003089 FALLTHROUGH_INTENDED;
Narayan Kamath788fa412014-01-21 15:32:36 +00003090 case 5:
3091 case 6:
3092 case 7:
3093 case 8:
3094 for (size_t i = 0; i < size; ++i) {
3095 config->localeVariant[i] = tolower(start[i]);
3096 }
3097 break;
3098 default:
Igor Viarheichyke7bc60a2017-10-20 15:09:13 -07003099 state.parserState = LocaleParserState::IGNORE_THE_REST;
Narayan Kamath788fa412014-01-21 15:32:36 +00003100 }
3101
Igor Viarheichyke7bc60a2017-10-20 15:09:13 -07003102 return state;
Narayan Kamath788fa412014-01-21 15:32:36 +00003103}
3104
3105void ResTable_config::setBcp47Locale(const char* in) {
Igor Viarheichyk7ec28a82017-11-10 11:58:38 -08003106 clearLocale();
Narayan Kamath788fa412014-01-21 15:32:36 +00003107
Narayan Kamath788fa412014-01-21 15:32:36 +00003108 const char* start = in;
Igor Viarheichyke7bc60a2017-10-20 15:09:13 -07003109 LocaleParserState state;
3110 while (const char* separator = strchr(start, '-')) {
Narayan Kamath788fa412014-01-21 15:32:36 +00003111 const size_t size = separator - start;
Igor Viarheichyke7bc60a2017-10-20 15:09:13 -07003112 state = assignLocaleComponent(this, start, size, state);
3113 if (state.parserState == LocaleParserState::IGNORE_THE_REST) {
3114 fprintf(stderr, "Invalid BCP-47 locale string: %s\n", in);
3115 break;
Narayan Kamath788fa412014-01-21 15:32:36 +00003116 }
Narayan Kamath788fa412014-01-21 15:32:36 +00003117 start = (separator + 1);
3118 }
3119
Igor Viarheichyke7bc60a2017-10-20 15:09:13 -07003120 if (state.parserState != LocaleParserState::IGNORE_THE_REST) {
3121 const size_t size = strlen(start);
3122 assignLocaleComponent(this, start, size, state);
3123 }
3124
Roozbeh Pournader79608982016-03-03 15:06:46 -08003125 localeScriptWasComputed = (localeScript[0] == '\0');
3126 if (localeScriptWasComputed) {
Roozbeh Pournaderb927c552016-01-15 11:23:42 -08003127 computeScript();
Roozbeh Pournader79608982016-03-03 15:06:46 -08003128 }
Narayan Kamath788fa412014-01-21 15:32:36 +00003129}
3130
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003131String8 ResTable_config::toString() const {
3132 String8 res;
3133
3134 if (mcc != 0) {
3135 if (res.size() > 0) res.append("-");
Adam Lesinskifab50872014-04-16 14:40:42 -07003136 res.appendFormat("mcc%d", dtohs(mcc));
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003137 }
3138 if (mnc != 0) {
3139 if (res.size() > 0) res.append("-");
Adam Lesinskifab50872014-04-16 14:40:42 -07003140 res.appendFormat("mnc%d", dtohs(mnc));
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003141 }
Adam Lesinskifab50872014-04-16 14:40:42 -07003142
Adam Lesinski8a9355a2015-03-10 16:55:43 -07003143 appendDirLocale(res);
Narayan Kamath48620f12014-01-20 13:57:11 +00003144
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07003145 if ((screenLayout&MASK_LAYOUTDIR) != 0) {
3146 if (res.size() > 0) res.append("-");
3147 switch (screenLayout&ResTable_config::MASK_LAYOUTDIR) {
3148 case ResTable_config::LAYOUTDIR_LTR:
Fabrice Di Meglio8a802db2012-09-05 13:12:02 -07003149 res.append("ldltr");
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07003150 break;
3151 case ResTable_config::LAYOUTDIR_RTL:
Fabrice Di Meglio8a802db2012-09-05 13:12:02 -07003152 res.append("ldrtl");
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07003153 break;
3154 default:
3155 res.appendFormat("layoutDir=%d",
3156 dtohs(screenLayout&ResTable_config::MASK_LAYOUTDIR));
3157 break;
3158 }
3159 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003160 if (smallestScreenWidthDp != 0) {
3161 if (res.size() > 0) res.append("-");
3162 res.appendFormat("sw%ddp", dtohs(smallestScreenWidthDp));
3163 }
3164 if (screenWidthDp != 0) {
3165 if (res.size() > 0) res.append("-");
3166 res.appendFormat("w%ddp", dtohs(screenWidthDp));
3167 }
3168 if (screenHeightDp != 0) {
3169 if (res.size() > 0) res.append("-");
3170 res.appendFormat("h%ddp", dtohs(screenHeightDp));
3171 }
3172 if ((screenLayout&MASK_SCREENSIZE) != SCREENSIZE_ANY) {
3173 if (res.size() > 0) res.append("-");
3174 switch (screenLayout&ResTable_config::MASK_SCREENSIZE) {
3175 case ResTable_config::SCREENSIZE_SMALL:
3176 res.append("small");
3177 break;
3178 case ResTable_config::SCREENSIZE_NORMAL:
3179 res.append("normal");
3180 break;
3181 case ResTable_config::SCREENSIZE_LARGE:
3182 res.append("large");
3183 break;
3184 case ResTable_config::SCREENSIZE_XLARGE:
3185 res.append("xlarge");
3186 break;
3187 default:
3188 res.appendFormat("screenLayoutSize=%d",
3189 dtohs(screenLayout&ResTable_config::MASK_SCREENSIZE));
3190 break;
3191 }
3192 }
3193 if ((screenLayout&MASK_SCREENLONG) != 0) {
3194 if (res.size() > 0) res.append("-");
3195 switch (screenLayout&ResTable_config::MASK_SCREENLONG) {
3196 case ResTable_config::SCREENLONG_NO:
3197 res.append("notlong");
3198 break;
3199 case ResTable_config::SCREENLONG_YES:
3200 res.append("long");
3201 break;
3202 default:
3203 res.appendFormat("screenLayoutLong=%d",
3204 dtohs(screenLayout&ResTable_config::MASK_SCREENLONG));
3205 break;
3206 }
3207 }
Adam Lesinski2738c962015-05-14 14:25:36 -07003208 if ((screenLayout2&MASK_SCREENROUND) != 0) {
3209 if (res.size() > 0) res.append("-");
3210 switch (screenLayout2&MASK_SCREENROUND) {
3211 case SCREENROUND_NO:
3212 res.append("notround");
3213 break;
3214 case SCREENROUND_YES:
3215 res.append("round");
3216 break;
3217 default:
3218 res.appendFormat("screenRound=%d", dtohs(screenLayout2&MASK_SCREENROUND));
3219 break;
3220 }
3221 }
Romain Guy48327452017-01-23 17:03:35 -08003222 if ((colorMode&MASK_WIDE_COLOR_GAMUT) != 0) {
Romain Guyc9ba5592017-01-18 16:34:42 -08003223 if (res.size() > 0) res.append("-");
Romain Guy48327452017-01-23 17:03:35 -08003224 switch (colorMode&MASK_WIDE_COLOR_GAMUT) {
Romain Guyc9ba5592017-01-18 16:34:42 -08003225 case ResTable_config::WIDE_COLOR_GAMUT_NO:
3226 res.append("nowidecg");
3227 break;
3228 case ResTable_config::WIDE_COLOR_GAMUT_YES:
3229 res.append("widecg");
3230 break;
3231 default:
Romain Guy48327452017-01-23 17:03:35 -08003232 res.appendFormat("wideColorGamut=%d", dtohs(colorMode&MASK_WIDE_COLOR_GAMUT));
Romain Guyc9ba5592017-01-18 16:34:42 -08003233 break;
3234 }
3235 }
Chia-I Wu0d8acf52018-05-09 12:08:05 -07003236 if ((colorMode&MASK_HDR) != 0) {
3237 if (res.size() > 0) res.append("-");
3238 switch (colorMode&MASK_HDR) {
3239 case ResTable_config::HDR_NO:
3240 res.append("lowdr");
3241 break;
3242 case ResTable_config::HDR_YES:
3243 res.append("highdr");
3244 break;
3245 default:
3246 res.appendFormat("hdr=%d", dtohs(colorMode&MASK_HDR));
3247 break;
3248 }
3249 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003250 if (orientation != ORIENTATION_ANY) {
3251 if (res.size() > 0) res.append("-");
3252 switch (orientation) {
3253 case ResTable_config::ORIENTATION_PORT:
3254 res.append("port");
3255 break;
3256 case ResTable_config::ORIENTATION_LAND:
3257 res.append("land");
3258 break;
3259 case ResTable_config::ORIENTATION_SQUARE:
3260 res.append("square");
3261 break;
3262 default:
3263 res.appendFormat("orientation=%d", dtohs(orientation));
3264 break;
3265 }
3266 }
3267 if ((uiMode&MASK_UI_MODE_TYPE) != UI_MODE_TYPE_ANY) {
3268 if (res.size() > 0) res.append("-");
3269 switch (uiMode&ResTable_config::MASK_UI_MODE_TYPE) {
3270 case ResTable_config::UI_MODE_TYPE_DESK:
3271 res.append("desk");
3272 break;
3273 case ResTable_config::UI_MODE_TYPE_CAR:
3274 res.append("car");
3275 break;
3276 case ResTable_config::UI_MODE_TYPE_TELEVISION:
3277 res.append("television");
3278 break;
3279 case ResTable_config::UI_MODE_TYPE_APPLIANCE:
3280 res.append("appliance");
3281 break;
John Spurlock6c191292014-04-03 16:37:27 -04003282 case ResTable_config::UI_MODE_TYPE_WATCH:
3283 res.append("watch");
3284 break;
Zak Cohen1a6acdb2016-12-12 15:21:21 -08003285 case ResTable_config::UI_MODE_TYPE_VR_HEADSET:
3286 res.append("vrheadset");
3287 break;
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003288 default:
3289 res.appendFormat("uiModeType=%d",
3290 dtohs(screenLayout&ResTable_config::MASK_UI_MODE_TYPE));
3291 break;
3292 }
3293 }
3294 if ((uiMode&MASK_UI_MODE_NIGHT) != 0) {
3295 if (res.size() > 0) res.append("-");
3296 switch (uiMode&ResTable_config::MASK_UI_MODE_NIGHT) {
3297 case ResTable_config::UI_MODE_NIGHT_NO:
3298 res.append("notnight");
3299 break;
3300 case ResTable_config::UI_MODE_NIGHT_YES:
3301 res.append("night");
3302 break;
3303 default:
3304 res.appendFormat("uiModeNight=%d",
3305 dtohs(uiMode&MASK_UI_MODE_NIGHT));
3306 break;
3307 }
3308 }
3309 if (density != DENSITY_DEFAULT) {
3310 if (res.size() > 0) res.append("-");
3311 switch (density) {
3312 case ResTable_config::DENSITY_LOW:
3313 res.append("ldpi");
3314 break;
3315 case ResTable_config::DENSITY_MEDIUM:
3316 res.append("mdpi");
3317 break;
3318 case ResTable_config::DENSITY_TV:
3319 res.append("tvdpi");
3320 break;
3321 case ResTable_config::DENSITY_HIGH:
3322 res.append("hdpi");
3323 break;
3324 case ResTable_config::DENSITY_XHIGH:
3325 res.append("xhdpi");
3326 break;
3327 case ResTable_config::DENSITY_XXHIGH:
3328 res.append("xxhdpi");
3329 break;
Adam Lesinski8d5667d2014-08-13 21:02:57 -07003330 case ResTable_config::DENSITY_XXXHIGH:
3331 res.append("xxxhdpi");
3332 break;
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003333 case ResTable_config::DENSITY_NONE:
3334 res.append("nodpi");
3335 break;
Adam Lesinski31245b42014-08-22 19:10:56 -07003336 case ResTable_config::DENSITY_ANY:
3337 res.append("anydpi");
3338 break;
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003339 default:
Dianne Hackborn5c6dfeb2012-03-09 13:17:17 -08003340 res.appendFormat("%ddpi", dtohs(density));
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003341 break;
3342 }
3343 }
3344 if (touchscreen != TOUCHSCREEN_ANY) {
3345 if (res.size() > 0) res.append("-");
3346 switch (touchscreen) {
3347 case ResTable_config::TOUCHSCREEN_NOTOUCH:
3348 res.append("notouch");
3349 break;
3350 case ResTable_config::TOUCHSCREEN_FINGER:
3351 res.append("finger");
3352 break;
3353 case ResTable_config::TOUCHSCREEN_STYLUS:
3354 res.append("stylus");
3355 break;
3356 default:
3357 res.appendFormat("touchscreen=%d", dtohs(touchscreen));
3358 break;
3359 }
3360 }
Adam Lesinskifab50872014-04-16 14:40:42 -07003361 if ((inputFlags&MASK_KEYSHIDDEN) != 0) {
3362 if (res.size() > 0) res.append("-");
3363 switch (inputFlags&MASK_KEYSHIDDEN) {
3364 case ResTable_config::KEYSHIDDEN_NO:
3365 res.append("keysexposed");
3366 break;
3367 case ResTable_config::KEYSHIDDEN_YES:
3368 res.append("keyshidden");
3369 break;
3370 case ResTable_config::KEYSHIDDEN_SOFT:
3371 res.append("keyssoft");
3372 break;
3373 }
3374 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003375 if (keyboard != KEYBOARD_ANY) {
3376 if (res.size() > 0) res.append("-");
3377 switch (keyboard) {
3378 case ResTable_config::KEYBOARD_NOKEYS:
3379 res.append("nokeys");
3380 break;
3381 case ResTable_config::KEYBOARD_QWERTY:
3382 res.append("qwerty");
3383 break;
3384 case ResTable_config::KEYBOARD_12KEY:
3385 res.append("12key");
3386 break;
3387 default:
3388 res.appendFormat("keyboard=%d", dtohs(keyboard));
3389 break;
3390 }
3391 }
Adam Lesinskifab50872014-04-16 14:40:42 -07003392 if ((inputFlags&MASK_NAVHIDDEN) != 0) {
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003393 if (res.size() > 0) res.append("-");
Adam Lesinskifab50872014-04-16 14:40:42 -07003394 switch (inputFlags&MASK_NAVHIDDEN) {
3395 case ResTable_config::NAVHIDDEN_NO:
3396 res.append("navexposed");
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003397 break;
Adam Lesinskifab50872014-04-16 14:40:42 -07003398 case ResTable_config::NAVHIDDEN_YES:
3399 res.append("navhidden");
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003400 break;
Adam Lesinskifab50872014-04-16 14:40:42 -07003401 default:
3402 res.appendFormat("inputFlagsNavHidden=%d",
3403 dtohs(inputFlags&MASK_NAVHIDDEN));
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003404 break;
3405 }
3406 }
3407 if (navigation != NAVIGATION_ANY) {
3408 if (res.size() > 0) res.append("-");
3409 switch (navigation) {
3410 case ResTable_config::NAVIGATION_NONAV:
3411 res.append("nonav");
3412 break;
3413 case ResTable_config::NAVIGATION_DPAD:
3414 res.append("dpad");
3415 break;
3416 case ResTable_config::NAVIGATION_TRACKBALL:
3417 res.append("trackball");
3418 break;
3419 case ResTable_config::NAVIGATION_WHEEL:
3420 res.append("wheel");
3421 break;
3422 default:
3423 res.appendFormat("navigation=%d", dtohs(navigation));
3424 break;
3425 }
3426 }
Dianne Hackborn6c997a92012-01-31 11:27:43 -08003427 if (screenSize != 0) {
3428 if (res.size() > 0) res.append("-");
3429 res.appendFormat("%dx%d", dtohs(screenWidth), dtohs(screenHeight));
3430 }
3431 if (version != 0) {
3432 if (res.size() > 0) res.append("-");
3433 res.appendFormat("v%d", dtohs(sdkVersion));
3434 if (minorVersion != 0) {
3435 res.appendFormat(".%d", dtohs(minorVersion));
3436 }
3437 }
3438
3439 return res;
3440}
3441
3442// --------------------------------------------------------------------
3443// --------------------------------------------------------------------
3444// --------------------------------------------------------------------
3445
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003446struct ResTable::Header
3447{
Chih-Hung Hsiehc6baf562016-04-27 11:29:23 -07003448 explicit Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL),
Mårten Kongstad57f4b772011-03-17 14:13:41 +01003449 resourceIDMap(NULL), resourceIDMapSize(0) { }
3450
3451 ~Header()
3452 {
3453 free(resourceIDMap);
3454 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003455
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003456 const ResTable* const owner;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003457 void* ownedData;
3458 const ResTable_header* header;
3459 size_t size;
3460 const uint8_t* dataEnd;
3461 size_t index;
Narayan Kamath7c4887f2014-01-27 17:32:37 +00003462 int32_t cookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003463
3464 ResStringPool values;
Mårten Kongstad57f4b772011-03-17 14:13:41 +01003465 uint32_t* resourceIDMap;
3466 size_t resourceIDMapSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003467};
3468
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003469struct ResTable::Entry {
3470 ResTable_config config;
3471 const ResTable_entry* entry;
3472 const ResTable_type* type;
3473 uint32_t specFlags;
3474 const Package* package;
3475
3476 StringPoolRef typeStr;
3477 StringPoolRef keyStr;
3478};
3479
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003480struct ResTable::Type
3481{
3482 Type(const Header* _header, const Package* _package, size_t count)
3483 : header(_header), package(_package), entryCount(count),
3484 typeSpec(NULL), typeSpecFlags(NULL) { }
3485 const Header* const header;
3486 const Package* const package;
3487 const size_t entryCount;
3488 const ResTable_typeSpec* typeSpec;
3489 const uint32_t* typeSpecFlags;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003490 IdmapEntries idmapEntries;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003491 Vector<const ResTable_type*> configs;
3492};
3493
3494struct ResTable::Package
3495{
Dianne Hackborn78c40512009-07-06 11:07:40 -07003496 Package(ResTable* _owner, const Header* _header, const ResTable_package* _package)
Adam Lesinski18560882014-08-15 17:18:21 +00003497 : owner(_owner), header(_header), package(_package), typeIdOffset(0) {
Wan He3ff42262016-11-17 17:49:37 +08003498 if (dtohs(package->header.headerSize) == sizeof(*package)) {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003499 // The package structure is the same size as the definition.
3500 // This means it contains the typeIdOffset field.
Adam Lesinski18560882014-08-15 17:18:21 +00003501 typeIdOffset = package->typeIdOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003502 }
3503 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07003504
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003505 const ResTable* const owner;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003506 const Header* const header;
Adam Lesinski18560882014-08-15 17:18:21 +00003507 const ResTable_package* const package;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003508
Dianne Hackborn78c40512009-07-06 11:07:40 -07003509 ResStringPool typeStrings;
3510 ResStringPool keyStrings;
Mark Salyzyn00adb862014-03-19 11:00:06 -07003511
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003512 size_t typeIdOffset;
Winson1201ca72019-04-12 16:19:26 -07003513 bool definesOverlayable = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003514};
3515
3516// A group of objects describing a particular resource package.
3517// The first in 'package' is always the root object (from the resource
3518// table that defined the package); the ones after are skins on top of it.
3519struct ResTable::PackageGroup
3520{
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08003521 PackageGroup(
3522 ResTable* _owner, const String16& _name, uint32_t _id,
Todd Kennedy32512992018-04-25 16:45:59 -07003523 bool appAsLib, bool _isSystemAsset, bool _isDynamic)
Adam Lesinskide898ff2014-01-29 18:20:45 -08003524 : owner(_owner)
3525 , name(_name)
3526 , id(_id)
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003527 , largestTypeId(0)
Tao Baia6d7e3f2015-09-01 18:49:54 -07003528 , dynamicRefTable(static_cast<uint8_t>(_id), appAsLib)
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08003529 , isSystemAsset(_isSystemAsset)
Todd Kennedy32512992018-04-25 16:45:59 -07003530 , isDynamic(_isDynamic)
Adam Lesinskide898ff2014-01-29 18:20:45 -08003531 { }
3532
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003533 ~PackageGroup() {
3534 clearBagCache();
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003535 const size_t numTypes = types.size();
3536 for (size_t i = 0; i < numTypes; i++) {
Sean Lu83df8422017-06-26 18:19:28 +08003537 TypeList& typeList = types.editItemAt(i);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003538 const size_t numInnerTypes = typeList.size();
3539 for (size_t j = 0; j < numInnerTypes; j++) {
3540 if (typeList[j]->package->owner == owner) {
3541 delete typeList[j];
3542 }
3543 }
Sean Lu83df8422017-06-26 18:19:28 +08003544 typeList.clear();
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003545 }
3546
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003547 const size_t N = packages.size();
3548 for (size_t i=0; i<N; i++) {
Dianne Hackborn78c40512009-07-06 11:07:40 -07003549 Package* pkg = packages[i];
3550 if (pkg->owner == owner) {
3551 delete pkg;
3552 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003553 }
3554 }
3555
Adam Lesinskiff5808d2016-02-23 17:49:53 -08003556 /**
3557 * Clear all cache related data that depends on parameters/configuration.
3558 * This includes the bag caches and filtered types.
3559 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003560 void clearBagCache() {
Adam Lesinskiff5808d2016-02-23 17:49:53 -08003561 for (size_t i = 0; i < typeCacheEntries.size(); i++) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003562 if (kDebugTableNoisy) {
Adam Lesinskiff5808d2016-02-23 17:49:53 -08003563 printf("type=%zu\n", i);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003564 }
Adam Lesinskiff5808d2016-02-23 17:49:53 -08003565 const TypeList& typeList = types[i];
3566 if (!typeList.isEmpty()) {
3567 TypeCacheEntry& cacheEntry = typeCacheEntries.editItemAt(i);
3568
3569 // Reset the filtered configurations.
3570 cacheEntry.filteredConfigs.clear();
3571
3572 bag_set** typeBags = cacheEntry.cachedBags;
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003573 if (kDebugTableNoisy) {
Adam Lesinskiff5808d2016-02-23 17:49:53 -08003574 printf("typeBags=%p\n", typeBags);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003575 }
Adam Lesinskiff5808d2016-02-23 17:49:53 -08003576
3577 if (typeBags) {
3578 const size_t N = typeList[0]->entryCount;
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003579 if (kDebugTableNoisy) {
Adam Lesinskiff5808d2016-02-23 17:49:53 -08003580 printf("type->entryCount=%zu\n", N);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003581 }
Adam Lesinskiff5808d2016-02-23 17:49:53 -08003582 for (size_t j = 0; j < N; j++) {
3583 if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF) {
3584 free(typeBags[j]);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003585 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003586 }
Adam Lesinskiff5808d2016-02-23 17:49:53 -08003587 free(typeBags);
3588 cacheEntry.cachedBags = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003589 }
3590 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003591 }
3592 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07003593
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003594 ssize_t findType16(const char16_t* type, size_t len) const {
3595 const size_t N = packages.size();
3596 for (size_t i = 0; i < N; i++) {
3597 ssize_t index = packages[i]->typeStrings.indexOfString(type, len);
3598 if (index >= 0) {
3599 return index + packages[i]->typeIdOffset;
3600 }
3601 }
3602 return -1;
3603 }
3604
3605 const ResTable* const owner;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003606 String16 const name;
3607 uint32_t const id;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003608
3609 // This is mainly used to keep track of the loaded packages
3610 // and to clean them up properly. Accessing resources happens from
3611 // the 'types' array.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003612 Vector<Package*> packages;
Mark Salyzyn00adb862014-03-19 11:00:06 -07003613
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003614 ByteBucketArray<TypeList> types;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003615
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003616 uint8_t largestTypeId;
Mark Salyzyn00adb862014-03-19 11:00:06 -07003617
Adam Lesinskiff5808d2016-02-23 17:49:53 -08003618 // Cached objects dependent on the parameters/configuration of this ResTable.
3619 // Gets cleared whenever the parameters/configuration changes.
3620 // These are stored here in a parallel structure because the data in `types` may
3621 // be shared by other ResTable's (framework resources are shared this way).
3622 ByteBucketArray<TypeCacheEntry> typeCacheEntries;
Adam Lesinskide898ff2014-01-29 18:20:45 -08003623
3624 // The table mapping dynamic references to resolved references for
3625 // this package group.
3626 // TODO: We may be able to support dynamic references in overlays
3627 // by having these tables in a per-package scope rather than
3628 // per-package-group.
3629 DynamicRefTable dynamicRefTable;
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08003630
3631 // If the package group comes from a system asset. Used in
3632 // determining non-system locales.
3633 const bool isSystemAsset;
Todd Kennedy32512992018-04-25 16:45:59 -07003634 const bool isDynamic;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003635};
3636
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003637ResTable::Theme::Theme(const ResTable& table)
3638 : mTable(table)
Alan Viverettec1d52792015-05-05 09:49:03 -07003639 , mTypeSpecFlags(0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003640{
3641 memset(mPackages, 0, sizeof(mPackages));
3642}
3643
3644ResTable::Theme::~Theme()
3645{
3646 for (size_t i=0; i<Res_MAXPACKAGE; i++) {
3647 package_info* pi = mPackages[i];
3648 if (pi != NULL) {
3649 free_package(pi);
3650 }
3651 }
3652}
3653
3654void ResTable::Theme::free_package(package_info* pi)
3655{
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003656 for (size_t j = 0; j <= Res_MAXTYPE; j++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003657 theme_entry* te = pi->types[j].entries;
3658 if (te != NULL) {
3659 free(te);
3660 }
3661 }
3662 free(pi);
3663}
3664
3665ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi)
3666{
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003667 package_info* newpi = (package_info*)malloc(sizeof(package_info));
3668 for (size_t j = 0; j <= Res_MAXTYPE; j++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003669 size_t cnt = pi->types[j].numEntries;
3670 newpi->types[j].numEntries = cnt;
3671 theme_entry* te = pi->types[j].entries;
Vishwath Mohan6a2c23d2015-03-09 18:55:11 -07003672 size_t cnt_max = SIZE_MAX / sizeof(theme_entry);
3673 if (te != NULL && (cnt < 0xFFFFFFFF-1) && (cnt < cnt_max)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003674 theme_entry* newte = (theme_entry*)malloc(cnt*sizeof(theme_entry));
3675 newpi->types[j].entries = newte;
3676 memcpy(newte, te, cnt*sizeof(theme_entry));
3677 } else {
3678 newpi->types[j].entries = NULL;
3679 }
3680 }
3681 return newpi;
3682}
3683
3684status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
3685{
3686 const bag_entry* bag;
3687 uint32_t bagTypeSpecFlags = 0;
3688 mTable.lock();
3689 const ssize_t N = mTable.getBagLocked(resID, &bag, &bagTypeSpecFlags);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003690 if (kDebugTableNoisy) {
3691 ALOGV("Applying style 0x%08x to theme %p, count=%zu", resID, this, N);
3692 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003693 if (N < 0) {
3694 mTable.unlock();
3695 return N;
3696 }
3697
Alan Viverettec1d52792015-05-05 09:49:03 -07003698 mTypeSpecFlags |= bagTypeSpecFlags;
3699
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003700 uint32_t curPackage = 0xffffffff;
3701 ssize_t curPackageIndex = 0;
3702 package_info* curPI = NULL;
3703 uint32_t curType = 0xffffffff;
3704 size_t numEntries = 0;
3705 theme_entry* curEntries = NULL;
3706
3707 const bag_entry* end = bag + N;
3708 while (bag < end) {
3709 const uint32_t attrRes = bag->map.name.ident;
3710 const uint32_t p = Res_GETPACKAGE(attrRes);
3711 const uint32_t t = Res_GETTYPE(attrRes);
3712 const uint32_t e = Res_GETENTRY(attrRes);
3713
3714 if (curPackage != p) {
3715 const ssize_t pidx = mTable.getResourcePackageIndex(attrRes);
3716 if (pidx < 0) {
Steve Block3762c312012-01-06 19:20:56 +00003717 ALOGE("Style contains key with bad package: 0x%08x\n", attrRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003718 bag++;
3719 continue;
3720 }
3721 curPackage = p;
3722 curPackageIndex = pidx;
3723 curPI = mPackages[pidx];
3724 if (curPI == NULL) {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003725 curPI = (package_info*)malloc(sizeof(package_info));
3726 memset(curPI, 0, sizeof(*curPI));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003727 mPackages[pidx] = curPI;
3728 }
3729 curType = 0xffffffff;
3730 }
3731 if (curType != t) {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003732 if (t > Res_MAXTYPE) {
Steve Block3762c312012-01-06 19:20:56 +00003733 ALOGE("Style contains key with bad type: 0x%08x\n", attrRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003734 bag++;
3735 continue;
3736 }
3737 curType = t;
3738 curEntries = curPI->types[t].entries;
3739 if (curEntries == NULL) {
3740 PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex];
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003741 const TypeList& typeList = grp->types[t];
Vishwath Mohan6a2c23d2015-03-09 18:55:11 -07003742 size_t cnt = typeList.isEmpty() ? 0 : typeList[0]->entryCount;
3743 size_t cnt_max = SIZE_MAX / sizeof(theme_entry);
3744 size_t buff_size = (cnt < cnt_max && cnt < 0xFFFFFFFF-1) ?
3745 cnt*sizeof(theme_entry) : 0;
3746 curEntries = (theme_entry*)malloc(buff_size);
3747 memset(curEntries, Res_value::TYPE_NULL, buff_size);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003748 curPI->types[t].numEntries = cnt;
3749 curPI->types[t].entries = curEntries;
3750 }
3751 numEntries = curPI->types[t].numEntries;
3752 }
3753 if (e >= numEntries) {
Steve Block3762c312012-01-06 19:20:56 +00003754 ALOGE("Style contains key with bad entry: 0x%08x\n", attrRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003755 bag++;
3756 continue;
3757 }
3758 theme_entry* curEntry = curEntries + e;
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003759 if (kDebugTableNoisy) {
3760 ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x",
3761 attrRes, bag->map.value.dataType, bag->map.value.data,
3762 curEntry->value.dataType);
3763 }
Adam Lesinski32e75012017-05-09 15:25:37 -07003764 if (force || (curEntry->value.dataType == Res_value::TYPE_NULL
3765 && curEntry->value.data != Res_value::DATA_NULL_EMPTY)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003766 curEntry->stringBlock = bag->stringBlock;
3767 curEntry->typeSpecFlags |= bagTypeSpecFlags;
3768 curEntry->value = bag->map.value;
3769 }
3770
3771 bag++;
3772 }
3773
3774 mTable.unlock();
3775
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003776 if (kDebugTableTheme) {
3777 ALOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this);
3778 dumpToLog();
3779 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07003780
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003781 return NO_ERROR;
3782}
3783
3784status_t ResTable::Theme::setTo(const Theme& other)
3785{
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003786 if (kDebugTableTheme) {
3787 ALOGI("Setting theme %p from theme %p...\n", this, &other);
3788 dumpToLog();
3789 other.dumpToLog();
3790 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07003791
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003792 if (&mTable == &other.mTable) {
3793 for (size_t i=0; i<Res_MAXPACKAGE; i++) {
3794 if (mPackages[i] != NULL) {
3795 free_package(mPackages[i]);
3796 }
3797 if (other.mPackages[i] != NULL) {
3798 mPackages[i] = copy_package(other.mPackages[i]);
3799 } else {
3800 mPackages[i] = NULL;
3801 }
3802 }
3803 } else {
3804 // @todo: need to really implement this, not just copy
3805 // the system package (which is still wrong because it isn't
3806 // fixing up resource references).
3807 for (size_t i=0; i<Res_MAXPACKAGE; i++) {
3808 if (mPackages[i] != NULL) {
3809 free_package(mPackages[i]);
3810 }
3811 if (i == 0 && other.mPackages[i] != NULL) {
3812 mPackages[i] = copy_package(other.mPackages[i]);
3813 } else {
3814 mPackages[i] = NULL;
3815 }
3816 }
3817 }
3818
Alan Viverettec1d52792015-05-05 09:49:03 -07003819 mTypeSpecFlags = other.mTypeSpecFlags;
3820
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003821 if (kDebugTableTheme) {
3822 ALOGI("Final theme:");
3823 dumpToLog();
3824 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07003825
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003826 return NO_ERROR;
3827}
3828
Alan Viverettee54d2452015-05-06 10:41:43 -07003829status_t ResTable::Theme::clear()
3830{
3831 if (kDebugTableTheme) {
3832 ALOGI("Clearing theme %p...\n", this);
3833 dumpToLog();
3834 }
3835
3836 for (size_t i = 0; i < Res_MAXPACKAGE; i++) {
3837 if (mPackages[i] != NULL) {
3838 free_package(mPackages[i]);
3839 mPackages[i] = NULL;
3840 }
3841 }
3842
3843 mTypeSpecFlags = 0;
3844
3845 if (kDebugTableTheme) {
3846 ALOGI("Final theme:");
3847 dumpToLog();
3848 }
3849
3850 return NO_ERROR;
3851}
3852
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003853ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
3854 uint32_t* outTypeSpecFlags) const
3855{
3856 int cnt = 20;
3857
3858 if (outTypeSpecFlags != NULL) *outTypeSpecFlags = 0;
Mark Salyzyn00adb862014-03-19 11:00:06 -07003859
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003860 do {
3861 const ssize_t p = mTable.getResourcePackageIndex(resID);
3862 const uint32_t t = Res_GETTYPE(resID);
3863 const uint32_t e = Res_GETENTRY(resID);
3864
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003865 if (kDebugTableTheme) {
3866 ALOGI("Looking up attr 0x%08x in theme %p", resID, this);
3867 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003868
3869 if (p >= 0) {
3870 const package_info* const pi = mPackages[p];
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003871 if (kDebugTableTheme) {
3872 ALOGI("Found package: %p", pi);
3873 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003874 if (pi != NULL) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003875 if (kDebugTableTheme) {
John Reckf6113af2016-11-03 16:16:47 -07003876 ALOGI("Desired type index is %u in avail %zu", t, Res_MAXTYPE + 1);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003877 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003878 if (t <= Res_MAXTYPE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003879 const type_info& ti = pi->types[t];
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003880 if (kDebugTableTheme) {
3881 ALOGI("Desired entry index is %u in avail %zu", e, ti.numEntries);
3882 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003883 if (e < ti.numEntries) {
3884 const theme_entry& te = ti.entries[e];
Dianne Hackbornb8d81672009-11-20 14:26:42 -08003885 if (outTypeSpecFlags != NULL) {
3886 *outTypeSpecFlags |= te.typeSpecFlags;
3887 }
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003888 if (kDebugTableTheme) {
3889 ALOGI("Theme value: type=0x%x, data=0x%08x",
3890 te.value.dataType, te.value.data);
3891 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003892 const uint8_t type = te.value.dataType;
3893 if (type == Res_value::TYPE_ATTRIBUTE) {
3894 if (cnt > 0) {
3895 cnt--;
3896 resID = te.value.data;
3897 continue;
3898 }
Steve Block8564c8d2012-01-05 23:22:43 +00003899 ALOGW("Too many attribute references, stopped at: 0x%08x\n", resID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003900 return BAD_INDEX;
Adam Lesinski32e75012017-05-09 15:25:37 -07003901 } else if (type != Res_value::TYPE_NULL
3902 || te.value.data == Res_value::DATA_NULL_EMPTY) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003903 *outValue = te.value;
3904 return te.stringBlock;
3905 }
3906 return BAD_INDEX;
3907 }
3908 }
3909 }
3910 }
3911 break;
3912
3913 } while (true);
3914
3915 return BAD_INDEX;
3916}
3917
3918ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue,
3919 ssize_t blockIndex, uint32_t* outLastRef,
Dianne Hackborn0d221012009-07-29 15:41:19 -07003920 uint32_t* inoutTypeSpecFlags, ResTable_config* inoutConfig) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003921{
3922 //printf("Resolving type=0x%x\n", inOutValue->dataType);
3923 if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) {
3924 uint32_t newTypeSpecFlags;
3925 blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003926 if (kDebugTableTheme) {
3927 ALOGI("Resolving attr reference: blockIndex=%d, type=0x%x, data=0x%x\n",
3928 (int)blockIndex, (int)inOutValue->dataType, inOutValue->data);
3929 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003930 if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags;
3931 //printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType);
3932 if (blockIndex < 0) {
3933 return blockIndex;
3934 }
3935 }
Dianne Hackborn0d221012009-07-29 15:41:19 -07003936 return mTable.resolveReference(inOutValue, blockIndex, outLastRef,
3937 inoutTypeSpecFlags, inoutConfig);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003938}
3939
Alan Viverettec1d52792015-05-05 09:49:03 -07003940uint32_t ResTable::Theme::getChangingConfigurations() const
3941{
3942 return mTypeSpecFlags;
3943}
3944
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003945void ResTable::Theme::dumpToLog() const
3946{
Steve Block6215d3f2012-01-04 20:05:49 +00003947 ALOGI("Theme %p:\n", this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003948 for (size_t i=0; i<Res_MAXPACKAGE; i++) {
3949 package_info* pi = mPackages[i];
3950 if (pi == NULL) continue;
Mark Salyzyn00adb862014-03-19 11:00:06 -07003951
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003952 ALOGI(" Package #0x%02x:\n", (int)(i + 1));
3953 for (size_t j = 0; j <= Res_MAXTYPE; j++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003954 type_info& ti = pi->types[j];
3955 if (ti.numEntries == 0) continue;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07003956 ALOGI(" Type #0x%02x:\n", (int)(j + 1));
3957 for (size_t k = 0; k < ti.numEntries; k++) {
3958 const theme_entry& te = ti.entries[k];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003959 if (te.value.dataType == Res_value::TYPE_NULL) continue;
Steve Block6215d3f2012-01-04 20:05:49 +00003960 ALOGI(" 0x%08x: t=0x%x, d=0x%08x (block=%d)\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003961 (int)Res_MAKEID(i, j, k),
3962 te.value.dataType, (int)te.value.data, (int)te.stringBlock);
3963 }
3964 }
3965 }
3966}
3967
3968ResTable::ResTable()
Adam Lesinskide898ff2014-01-29 18:20:45 -08003969 : mError(NO_INIT), mNextPackageId(2)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003970{
3971 memset(&mParams, 0, sizeof(mParams));
3972 memset(mPackageMap, 0, sizeof(mPackageMap));
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003973 if (kDebugTableSuperNoisy) {
3974 ALOGI("Creating ResTable %p\n", this);
3975 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003976}
3977
Narayan Kamath7c4887f2014-01-27 17:32:37 +00003978ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool copyData)
Adam Lesinskide898ff2014-01-29 18:20:45 -08003979 : mError(NO_INIT), mNextPackageId(2)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003980{
3981 memset(&mParams, 0, sizeof(mParams));
3982 memset(mPackageMap, 0, sizeof(mPackageMap));
Tao Baia6d7e3f2015-09-01 18:49:54 -07003983 addInternal(data, size, NULL, 0, false, cookie, copyData);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003984 LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003985 if (kDebugTableSuperNoisy) {
3986 ALOGI("Creating ResTable %p\n", this);
3987 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003988}
3989
3990ResTable::~ResTable()
3991{
Andreas Gampe2204f0b2014-10-21 23:04:54 -07003992 if (kDebugTableSuperNoisy) {
3993 ALOGI("Destroying ResTable in %p\n", this);
3994 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003995 uninit();
3996}
3997
3998inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const
3999{
4000 return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1;
4001}
4002
Todd Kennedy32512992018-04-25 16:45:59 -07004003inline ssize_t ResTable::getResourcePackageIndexFromPackage(uint8_t packageID) const
4004{
4005 return ((ssize_t)mPackageMap[packageID])-1;
4006}
4007
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004008status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) {
Tao Baia6d7e3f2015-09-01 18:49:54 -07004009 return addInternal(data, size, NULL, 0, false, cookie, copyData);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004010}
4011
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004012status_t ResTable::add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
Tao Baia6d7e3f2015-09-01 18:49:54 -07004013 const int32_t cookie, bool copyData, bool appAsLib) {
4014 return addInternal(data, size, idmapData, idmapDataSize, appAsLib, cookie, copyData);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004015}
4016
4017status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004018 const void* data = asset->getBuffer(true);
4019 if (data == NULL) {
Steve Block8564c8d2012-01-05 23:22:43 +00004020 ALOGW("Unable to get buffer of resource asset file");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004021 return UNKNOWN_ERROR;
4022 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004023
Tao Baia6d7e3f2015-09-01 18:49:54 -07004024 return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, false, 0, cookie,
4025 copyData);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004026}
4027
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08004028status_t ResTable::add(
4029 Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData,
4030 bool appAsLib, bool isSystemAsset) {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004031 const void* data = asset->getBuffer(true);
4032 if (data == NULL) {
4033 ALOGW("Unable to get buffer of resource asset file");
4034 return UNKNOWN_ERROR;
4035 }
4036
4037 size_t idmapSize = 0;
4038 const void* idmapData = NULL;
4039 if (idmapAsset != NULL) {
4040 idmapData = idmapAsset->getBuffer(true);
4041 if (idmapData == NULL) {
4042 ALOGW("Unable to get buffer of idmap asset file");
4043 return UNKNOWN_ERROR;
4044 }
4045 idmapSize = static_cast<size_t>(idmapAsset->getLength());
4046 }
4047
4048 return addInternal(data, static_cast<size_t>(asset->getLength()),
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08004049 idmapData, idmapSize, appAsLib, cookie, copyData, isSystemAsset);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004050}
4051
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08004052status_t ResTable::add(ResTable* src, bool isSystemAsset)
Dianne Hackborn78c40512009-07-06 11:07:40 -07004053{
4054 mError = src->mError;
Mark Salyzyn00adb862014-03-19 11:00:06 -07004055
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08004056 for (size_t i=0; i < src->mHeaders.size(); i++) {
Dianne Hackborn78c40512009-07-06 11:07:40 -07004057 mHeaders.add(src->mHeaders[i]);
4058 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07004059
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08004060 for (size_t i=0; i < src->mPackageGroups.size(); i++) {
Dianne Hackborn78c40512009-07-06 11:07:40 -07004061 PackageGroup* srcPg = src->mPackageGroups[i];
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08004062 PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id,
Todd Kennedy32512992018-04-25 16:45:59 -07004063 false /* appAsLib */, isSystemAsset || srcPg->isSystemAsset, srcPg->isDynamic);
Dianne Hackborn78c40512009-07-06 11:07:40 -07004064 for (size_t j=0; j<srcPg->packages.size(); j++) {
4065 pg->packages.add(srcPg->packages[j]);
4066 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004067
4068 for (size_t j = 0; j < srcPg->types.size(); j++) {
4069 if (srcPg->types[j].isEmpty()) {
4070 continue;
4071 }
4072
4073 TypeList& typeList = pg->types.editItemAt(j);
4074 typeList.appendVector(srcPg->types[j]);
4075 }
Adam Lesinski6022deb2014-08-20 14:59:19 -07004076 pg->dynamicRefTable.addMappings(srcPg->dynamicRefTable);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004077 pg->largestTypeId = max(pg->largestTypeId, srcPg->largestTypeId);
Dianne Hackborn78c40512009-07-06 11:07:40 -07004078 mPackageGroups.add(pg);
4079 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07004080
Dianne Hackborn78c40512009-07-06 11:07:40 -07004081 memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap));
Mark Salyzyn00adb862014-03-19 11:00:06 -07004082
Dianne Hackborn78c40512009-07-06 11:07:40 -07004083 return mError;
4084}
4085
Adam Lesinskide898ff2014-01-29 18:20:45 -08004086status_t ResTable::addEmpty(const int32_t cookie) {
4087 Header* header = new Header(this);
4088 header->index = mHeaders.size();
4089 header->cookie = cookie;
4090 header->values.setToEmpty();
4091 header->ownedData = calloc(1, sizeof(ResTable_header));
4092
4093 ResTable_header* resHeader = (ResTable_header*) header->ownedData;
4094 resHeader->header.type = RES_TABLE_TYPE;
4095 resHeader->header.headerSize = sizeof(ResTable_header);
4096 resHeader->header.size = sizeof(ResTable_header);
4097
4098 header->header = (const ResTable_header*) resHeader;
4099 mHeaders.add(header);
Adam Lesinski961dda72014-06-09 17:10:29 -07004100 return (mError=NO_ERROR);
Adam Lesinskide898ff2014-01-29 18:20:45 -08004101}
4102
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004103status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize,
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08004104 bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004105{
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004106 if (!data) {
4107 return NO_ERROR;
4108 }
4109
Adam Lesinskif28d5052014-07-25 15:25:04 -07004110 if (dataSize < sizeof(ResTable_header)) {
4111 ALOGE("Invalid data. Size(%d) is smaller than a ResTable_header(%d).",
4112 (int) dataSize, (int) sizeof(ResTable_header));
4113 return UNKNOWN_ERROR;
4114 }
4115
Dianne Hackborn78c40512009-07-06 11:07:40 -07004116 Header* header = new Header(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004117 header->index = mHeaders.size();
4118 header->cookie = cookie;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004119 if (idmapData != NULL) {
4120 header->resourceIDMap = (uint32_t*) malloc(idmapDataSize);
Mårten Kongstad57f4b772011-03-17 14:13:41 +01004121 if (header->resourceIDMap == NULL) {
4122 delete header;
4123 return (mError = NO_MEMORY);
4124 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004125 memcpy(header->resourceIDMap, idmapData, idmapDataSize);
4126 header->resourceIDMapSize = idmapDataSize;
Mårten Kongstad57f4b772011-03-17 14:13:41 +01004127 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004128 mHeaders.add(header);
4129
4130 const bool notDeviceEndian = htods(0xf0) != 0xf0;
4131
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004132 if (kDebugLoadTableNoisy) {
4133 ALOGV("Adding resources to ResTable: data=%p, size=%zu, cookie=%d, copy=%d "
4134 "idmap=%p\n", data, dataSize, cookie, copyData, idmapData);
4135 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07004136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004137 if (copyData || notDeviceEndian) {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004138 header->ownedData = malloc(dataSize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004139 if (header->ownedData == NULL) {
4140 return (mError=NO_MEMORY);
4141 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004142 memcpy(header->ownedData, data, dataSize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004143 data = header->ownedData;
4144 }
4145
4146 header->header = (const ResTable_header*)data;
4147 header->size = dtohl(header->header->header.size);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004148 if (kDebugLoadTableSuperNoisy) {
4149 ALOGI("Got size %zu, again size 0x%x, raw size 0x%x\n", header->size,
4150 dtohl(header->header->header.size), header->header->header.size);
4151 }
4152 if (kDebugLoadTableNoisy) {
4153 ALOGV("Loading ResTable @%p:\n", header->header);
4154 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004155 if (dtohs(header->header->header.headerSize) > header->size
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004156 || header->size > dataSize) {
Steve Block8564c8d2012-01-05 23:22:43 +00004157 ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004158 (int)dtohs(header->header->header.headerSize),
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004159 (int)header->size, (int)dataSize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004160 return (mError=BAD_TYPE);
4161 }
4162 if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) {
Steve Block8564c8d2012-01-05 23:22:43 +00004163 ALOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004164 (int)dtohs(header->header->header.headerSize),
4165 (int)header->size);
4166 return (mError=BAD_TYPE);
4167 }
4168 header->dataEnd = ((const uint8_t*)header->header) + header->size;
4169
4170 // Iterate through all chunks.
4171 size_t curPackage = 0;
4172
4173 const ResChunk_header* chunk =
4174 (const ResChunk_header*)(((const uint8_t*)header->header)
4175 + dtohs(header->header->header.headerSize));
4176 while (((const uint8_t*)chunk) <= (header->dataEnd-sizeof(ResChunk_header)) &&
4177 ((const uint8_t*)chunk) <= (header->dataEnd-dtohl(chunk->size))) {
4178 status_t err = validate_chunk(chunk, sizeof(ResChunk_header), header->dataEnd, "ResTable");
4179 if (err != NO_ERROR) {
4180 return (mError=err);
4181 }
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004182 if (kDebugTableNoisy) {
4183 ALOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n",
4184 dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size),
4185 (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)));
4186 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004187 const size_t csize = dtohl(chunk->size);
4188 const uint16_t ctype = dtohs(chunk->type);
4189 if (ctype == RES_STRING_POOL_TYPE) {
4190 if (header->values.getError() != NO_ERROR) {
4191 // Only use the first string chunk; ignore any others that
4192 // may appear.
4193 status_t err = header->values.setTo(chunk, csize);
4194 if (err != NO_ERROR) {
4195 return (mError=err);
4196 }
4197 } else {
Steve Block8564c8d2012-01-05 23:22:43 +00004198 ALOGW("Multiple string chunks found in resource table.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004199 }
4200 } else if (ctype == RES_TABLE_PACKAGE_TYPE) {
4201 if (curPackage >= dtohl(header->header->packageCount)) {
Steve Block8564c8d2012-01-05 23:22:43 +00004202 ALOGW("More package chunks were found than the %d declared in the header.",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004203 dtohl(header->header->packageCount));
4204 return (mError=BAD_TYPE);
4205 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004206
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08004207 if (parsePackage(
4208 (ResTable_package*)chunk, header, appAsLib, isSystemAsset) != NO_ERROR) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004209 return mError;
4210 }
4211 curPackage++;
4212 } else {
Patrik Bannura443dd932014-02-12 13:38:54 +01004213 ALOGW("Unknown chunk type 0x%x in table at %p.\n",
4214 ctype,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004215 (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)));
4216 }
4217 chunk = (const ResChunk_header*)
4218 (((const uint8_t*)chunk) + csize);
4219 }
4220
4221 if (curPackage < dtohl(header->header->packageCount)) {
Steve Block8564c8d2012-01-05 23:22:43 +00004222 ALOGW("Fewer package chunks (%d) were found than the %d declared in the header.",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004223 (int)curPackage, dtohl(header->header->packageCount));
4224 return (mError=BAD_TYPE);
4225 }
4226 mError = header->values.getError();
4227 if (mError != NO_ERROR) {
Steve Block8564c8d2012-01-05 23:22:43 +00004228 ALOGW("No string values found in resource table!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004229 }
Mårten Kongstad57f4b772011-03-17 14:13:41 +01004230
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004231 if (kDebugTableNoisy) {
4232 ALOGV("Returning from add with mError=%d\n", mError);
4233 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004234 return mError;
4235}
4236
4237status_t ResTable::getError() const
4238{
4239 return mError;
4240}
4241
4242void ResTable::uninit()
4243{
4244 mError = NO_INIT;
4245 size_t N = mPackageGroups.size();
4246 for (size_t i=0; i<N; i++) {
4247 PackageGroup* g = mPackageGroups[i];
4248 delete g;
4249 }
4250 N = mHeaders.size();
4251 for (size_t i=0; i<N; i++) {
4252 Header* header = mHeaders[i];
Dianne Hackborn78c40512009-07-06 11:07:40 -07004253 if (header->owner == this) {
4254 if (header->ownedData) {
4255 free(header->ownedData);
4256 }
4257 delete header;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004258 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004259 }
4260
4261 mPackageGroups.clear();
4262 mHeaders.clear();
4263}
4264
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07004265bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* outName) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004266{
4267 if (mError != NO_ERROR) {
4268 return false;
4269 }
4270
4271 const ssize_t p = getResourcePackageIndex(resID);
4272 const int t = Res_GETTYPE(resID);
4273 const int e = Res_GETENTRY(resID);
4274
4275 if (p < 0) {
Dianne Hackborn6cca1592009-09-20 12:40:03 -07004276 if (Res_GETPACKAGE(resID)+1 == 0) {
Steve Block8564c8d2012-01-05 23:22:43 +00004277 ALOGW("No package identifier when getting name for resource number 0x%08x", resID);
Dianne Hackborn6cca1592009-09-20 12:40:03 -07004278 } else {
Adam Lesinski1d7172e2016-03-30 16:22:33 -07004279#ifndef STATIC_ANDROIDFW_FOR_TOOLS
Steve Block8564c8d2012-01-05 23:22:43 +00004280 ALOGW("No known package when getting name for resource number 0x%08x", resID);
Adam Lesinski1d7172e2016-03-30 16:22:33 -07004281#endif
Dianne Hackborn6cca1592009-09-20 12:40:03 -07004282 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004283 return false;
4284 }
4285 if (t < 0) {
Steve Block8564c8d2012-01-05 23:22:43 +00004286 ALOGW("No type identifier when getting name for resource number 0x%08x", resID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004287 return false;
4288 }
4289
4290 const PackageGroup* const grp = mPackageGroups[p];
4291 if (grp == NULL) {
Steve Block8564c8d2012-01-05 23:22:43 +00004292 ALOGW("Bad identifier when getting name for resource number 0x%08x", resID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004293 return false;
4294 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004295
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004296 Entry entry;
4297 status_t err = getEntry(grp, t, e, NULL, &entry);
4298 if (err != NO_ERROR) {
4299 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004300 }
4301
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004302 outName->package = grp->name.string();
4303 outName->packageLen = grp->name.size();
4304 if (allowUtf8) {
4305 outName->type8 = entry.typeStr.string8(&outName->typeLen);
4306 outName->name8 = entry.keyStr.string8(&outName->nameLen);
4307 } else {
4308 outName->type8 = NULL;
4309 outName->name8 = NULL;
4310 }
4311 if (outName->type8 == NULL) {
4312 outName->type = entry.typeStr.string16(&outName->typeLen);
4313 // If we have a bad index for some reason, we should abort.
4314 if (outName->type == NULL) {
4315 return false;
4316 }
4317 }
4318 if (outName->name8 == NULL) {
4319 outName->name = entry.keyStr.string16(&outName->nameLen);
4320 // If we have a bad index for some reason, we should abort.
4321 if (outName->name == NULL) {
4322 return false;
4323 }
4324 }
4325
4326 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004327}
4328
Kenny Root55fc8502010-10-28 14:47:01 -07004329ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004330 uint32_t* outSpecFlags, ResTable_config* outConfig) const
4331{
4332 if (mError != NO_ERROR) {
4333 return mError;
4334 }
4335
4336 const ssize_t p = getResourcePackageIndex(resID);
4337 const int t = Res_GETTYPE(resID);
4338 const int e = Res_GETENTRY(resID);
4339
4340 if (p < 0) {
Dianne Hackborn6cca1592009-09-20 12:40:03 -07004341 if (Res_GETPACKAGE(resID)+1 == 0) {
Steve Block8564c8d2012-01-05 23:22:43 +00004342 ALOGW("No package identifier when getting value for resource number 0x%08x", resID);
Dianne Hackborn6cca1592009-09-20 12:40:03 -07004343 } else {
Steve Block8564c8d2012-01-05 23:22:43 +00004344 ALOGW("No known package when getting value for resource number 0x%08x", resID);
Dianne Hackborn6cca1592009-09-20 12:40:03 -07004345 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004346 return BAD_INDEX;
4347 }
4348 if (t < 0) {
Steve Block8564c8d2012-01-05 23:22:43 +00004349 ALOGW("No type identifier when getting value for resource number 0x%08x", resID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004350 return BAD_INDEX;
4351 }
4352
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004353 const PackageGroup* const grp = mPackageGroups[p];
4354 if (grp == NULL) {
Steve Block8564c8d2012-01-05 23:22:43 +00004355 ALOGW("Bad identifier when getting value for resource number 0x%08x", resID);
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08004356 return BAD_INDEX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004357 }
Kenny Root55fc8502010-10-28 14:47:01 -07004358
4359 // Allow overriding density
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004360 ResTable_config desiredConfig = mParams;
Kenny Root55fc8502010-10-28 14:47:01 -07004361 if (density > 0) {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004362 desiredConfig.density = density;
Kenny Root55fc8502010-10-28 14:47:01 -07004363 }
4364
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004365 Entry entry;
4366 status_t err = getEntry(grp, t, e, &desiredConfig, &entry);
4367 if (err != NO_ERROR) {
Adam Lesinskide7de472014-11-03 12:03:08 -08004368 // Only log the failure when we're not running on the host as
4369 // part of a tool. The caller will do its own logging.
4370#ifndef STATIC_ANDROIDFW_FOR_TOOLS
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004371 ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) (error %d)\n",
4372 resID, t, e, err);
Adam Lesinskide7de472014-11-03 12:03:08 -08004373#endif
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004374 return err;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004375 }
4376
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004377 if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) {
4378 if (!mayBeBag) {
4379 ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID);
Adam Lesinskide898ff2014-01-29 18:20:45 -08004380 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004381 return BAD_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004382 }
4383
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004384 const Res_value* value = reinterpret_cast<const Res_value*>(
4385 reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size);
4386
4387 outValue->size = dtohs(value->size);
4388 outValue->res0 = value->res0;
4389 outValue->dataType = value->dataType;
4390 outValue->data = dtohl(value->data);
4391
4392 // The reference may be pointing to a resource in a shared library. These
4393 // references have build-time generated package IDs. These ids may not match
4394 // the actual package IDs of the corresponding packages in this ResTable.
4395 // We need to fix the package ID based on a mapping.
4396 if (grp->dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) {
4397 ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data);
4398 return BAD_VALUE;
Kenny Root55fc8502010-10-28 14:47:01 -07004399 }
4400
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004401 if (kDebugTableNoisy) {
4402 size_t len;
4403 printf("Found value: pkg=%zu, type=%d, str=%s, int=%d\n",
4404 entry.package->header->index,
4405 outValue->dataType,
4406 outValue->dataType == Res_value::TYPE_STRING ?
4407 String8(entry.package->header->values.stringAt(outValue->data, &len)).string() :
4408 "",
4409 outValue->data);
4410 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004411
4412 if (outSpecFlags != NULL) {
4413 *outSpecFlags = entry.specFlags;
4414 }
4415
4416 if (outConfig != NULL) {
4417 *outConfig = entry.config;
4418 }
4419
4420 return entry.package->header->index;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004421}
4422
4423ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
Dianne Hackborn0d221012009-07-29 15:41:19 -07004424 uint32_t* outLastRef, uint32_t* inoutTypeSpecFlags,
4425 ResTable_config* outConfig) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004426{
4427 int count=0;
Adam Lesinskide898ff2014-01-29 18:20:45 -08004428 while (blockIndex >= 0 && value->dataType == Res_value::TYPE_REFERENCE
4429 && value->data != 0 && count < 20) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004430 if (outLastRef) *outLastRef = value->data;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004431 uint32_t newFlags = 0;
Kenny Root55fc8502010-10-28 14:47:01 -07004432 const ssize_t newIndex = getResource(value->data, value, true, 0, &newFlags,
Dianne Hackborn0d221012009-07-29 15:41:19 -07004433 outConfig);
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08004434 if (newIndex == BAD_INDEX) {
4435 return BAD_INDEX;
4436 }
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004437 if (kDebugTableTheme) {
4438 ALOGI("Resolving reference 0x%x: newIndex=%d, type=0x%x, data=0x%x\n",
4439 value->data, (int)newIndex, (int)value->dataType, value->data);
4440 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004441 //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex);
4442 if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags;
4443 if (newIndex < 0) {
4444 // This can fail if the resource being referenced is a style...
4445 // in this case, just return the reference, and expect the
4446 // caller to deal with.
4447 return blockIndex;
4448 }
4449 blockIndex = newIndex;
4450 count++;
4451 }
4452 return blockIndex;
4453}
4454
4455const char16_t* ResTable::valueToString(
4456 const Res_value* value, size_t stringBlock,
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07004457 char16_t /*tmpBuffer*/ [TMP_BUFFER_SIZE], size_t* outLen) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004458{
4459 if (!value) {
4460 return NULL;
4461 }
4462 if (value->dataType == value->TYPE_STRING) {
4463 return getTableStringBlock(stringBlock)->stringAt(value->data, outLen);
4464 }
4465 // XXX do int to string conversions.
4466 return NULL;
4467}
4468
4469ssize_t ResTable::lockBag(uint32_t resID, const bag_entry** outBag) const
4470{
4471 mLock.lock();
4472 ssize_t err = getBagLocked(resID, outBag);
4473 if (err < NO_ERROR) {
4474 //printf("*** get failed! unlocking\n");
4475 mLock.unlock();
4476 }
4477 return err;
4478}
4479
Mark Salyzyn00adb862014-03-19 11:00:06 -07004480void ResTable::unlockBag(const bag_entry* /*bag*/) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004481{
4482 //printf("<<< unlockBag %p\n", this);
4483 mLock.unlock();
4484}
4485
4486void ResTable::lock() const
4487{
4488 mLock.lock();
4489}
4490
4491void ResTable::unlock() const
4492{
4493 mLock.unlock();
4494}
4495
4496ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
4497 uint32_t* outTypeSpecFlags) const
4498{
4499 if (mError != NO_ERROR) {
4500 return mError;
4501 }
4502
4503 const ssize_t p = getResourcePackageIndex(resID);
4504 const int t = Res_GETTYPE(resID);
4505 const int e = Res_GETENTRY(resID);
4506
4507 if (p < 0) {
Steve Block8564c8d2012-01-05 23:22:43 +00004508 ALOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004509 return BAD_INDEX;
4510 }
4511 if (t < 0) {
Steve Block8564c8d2012-01-05 23:22:43 +00004512 ALOGW("No type identifier when getting bag for resource number 0x%08x", resID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004513 return BAD_INDEX;
4514 }
4515
4516 //printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t);
4517 PackageGroup* const grp = mPackageGroups[p];
4518 if (grp == NULL) {
Steve Block8564c8d2012-01-05 23:22:43 +00004519 ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004520 return BAD_INDEX;
4521 }
4522
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004523 const TypeList& typeConfigs = grp->types[t];
4524 if (typeConfigs.isEmpty()) {
4525 ALOGW("Type identifier 0x%x does not exist.", t+1);
4526 return BAD_INDEX;
4527 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004528
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004529 const size_t NENTRY = typeConfigs[0]->entryCount;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004530 if (e >= (int)NENTRY) {
Steve Block8564c8d2012-01-05 23:22:43 +00004531 ALOGW("Entry identifier 0x%x is larger than entry count 0x%x",
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004532 e, (int)typeConfigs[0]->entryCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004533 return BAD_INDEX;
4534 }
4535
4536 // First see if we've already computed this bag...
Adam Lesinskiff5808d2016-02-23 17:49:53 -08004537 TypeCacheEntry& cacheEntry = grp->typeCacheEntries.editItemAt(t);
4538 bag_set** typeSet = cacheEntry.cachedBags;
4539 if (typeSet) {
4540 bag_set* set = typeSet[e];
4541 if (set) {
4542 if (set != (bag_set*)0xFFFFFFFF) {
4543 if (outTypeSpecFlags != NULL) {
4544 *outTypeSpecFlags = set->typeSpecFlags;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004545 }
Adam Lesinskiff5808d2016-02-23 17:49:53 -08004546 *outBag = (bag_entry*)(set+1);
4547 if (kDebugTableSuperNoisy) {
4548 ALOGI("Found existing bag for: 0x%x\n", resID);
4549 }
4550 return set->numAttrs;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004551 }
Adam Lesinskiff5808d2016-02-23 17:49:53 -08004552 ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
4553 resID);
4554 return BAD_INDEX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004555 }
4556 }
4557
4558 // Bag not found, we need to compute it!
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004559 if (!typeSet) {
Iliyan Malchev7e1d3952012-02-17 12:15:58 -08004560 typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004561 if (!typeSet) return NO_MEMORY;
Adam Lesinskiff5808d2016-02-23 17:49:53 -08004562 cacheEntry.cachedBags = typeSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004563 }
4564
4565 // Mark that we are currently working on this one.
4566 typeSet[e] = (bag_set*)0xFFFFFFFF;
4567
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004568 if (kDebugTableNoisy) {
4569 ALOGI("Building bag: %x\n", resID);
4570 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004571
4572 // Now collect all bag attributes
4573 Entry entry;
4574 status_t err = getEntry(grp, t, e, &mParams, &entry);
4575 if (err != NO_ERROR) {
4576 return err;
4577 }
4578
4579 const uint16_t entrySize = dtohs(entry.entry->size);
4580 const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
4581 ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0;
4582 const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
4583 ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0;
4584
4585 size_t N = count;
4586
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004587 if (kDebugTableNoisy) {
4588 ALOGI("Found map: size=%x parent=%x count=%d\n", entrySize, parent, count);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004589
4590 // If this map inherits from another, we need to start
4591 // with its parent's values. Otherwise start out empty.
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004592 ALOGI("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", entrySize, parent);
4593 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004594
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004595 // This is what we are building.
4596 bag_set* set = NULL;
4597
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004598 if (parent) {
4599 uint32_t resolvedParent = parent;
Mark Salyzyn00adb862014-03-19 11:00:06 -07004600
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004601 // Bags encode a parent reference without using the standard
4602 // Res_value structure. That means we must always try to
4603 // resolve a parent reference in case it is actually a
4604 // TYPE_DYNAMIC_REFERENCE.
4605 status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent);
4606 if (err != NO_ERROR) {
4607 ALOGE("Failed resolving bag parent id 0x%08x", parent);
4608 return UNKNOWN_ERROR;
Mårten Kongstad57f4b772011-03-17 14:13:41 +01004609 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004610
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004611 const bag_entry* parentBag;
4612 uint32_t parentTypeSpecFlags = 0;
4613 const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags);
4614 const size_t NT = ((NP >= 0) ? NP : 0) + N;
4615 set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
4616 if (set == NULL) {
4617 return NO_MEMORY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004618 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004619 if (NP > 0) {
4620 memcpy(set+1, parentBag, NP*sizeof(bag_entry));
4621 set->numAttrs = NP;
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004622 if (kDebugTableNoisy) {
4623 ALOGI("Initialized new bag with %zd inherited attributes.\n", NP);
4624 }
Mårten Kongstad57f4b772011-03-17 14:13:41 +01004625 } else {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004626 if (kDebugTableNoisy) {
4627 ALOGI("Initialized new bag with no inherited attributes.\n");
4628 }
Mårten Kongstad57f4b772011-03-17 14:13:41 +01004629 set->numAttrs = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004630 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004631 set->availAttrs = NT;
4632 set->typeSpecFlags = parentTypeSpecFlags;
4633 } else {
4634 set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
4635 if (set == NULL) {
4636 return NO_MEMORY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004637 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004638 set->numAttrs = 0;
4639 set->availAttrs = N;
4640 set->typeSpecFlags = 0;
4641 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07004642
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004643 set->typeSpecFlags |= entry.specFlags;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004644
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004645 // Now merge in the new attributes...
4646 size_t curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type))
4647 + dtohs(entry.entry->size);
4648 const ResTable_map* map;
4649 bag_entry* entries = (bag_entry*)(set+1);
4650 size_t curEntry = 0;
4651 uint32_t pos = 0;
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004652 if (kDebugTableNoisy) {
4653 ALOGI("Starting with set %p, entries=%p, avail=%zu\n", set, entries, set->availAttrs);
4654 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004655 while (pos < count) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004656 if (kDebugTableNoisy) {
4657 ALOGI("Now at %p\n", (void*)curOff);
4658 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004659
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004660 if (curOff > (dtohl(entry.type->header.size)-sizeof(ResTable_map))) {
4661 ALOGW("ResTable_map at %d is beyond type chunk data %d",
4662 (int)curOff, dtohl(entry.type->header.size));
Yunlian Jianga7645bd2016-12-13 19:42:30 -08004663 free(set);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004664 return BAD_TYPE;
4665 }
4666 map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff);
4667 N++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004668
Adam Lesinskiccf25c7b2014-08-08 15:32:40 -07004669 uint32_t newName = htodl(map->name.ident);
4670 if (!Res_INTERNALID(newName)) {
4671 // Attributes don't have a resource id as the name. They specify
4672 // other data, which would be wrong to change via a lookup.
4673 if (grp->dynamicRefTable.lookupResourceId(&newName) != NO_ERROR) {
4674 ALOGE("Failed resolving ResTable_map name at %d with ident 0x%08x",
4675 (int) curOff, (int) newName);
Yunlian Jianga7645bd2016-12-13 19:42:30 -08004676 free(set);
Adam Lesinskiccf25c7b2014-08-08 15:32:40 -07004677 return UNKNOWN_ERROR;
4678 }
4679 }
4680
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004681 bool isInside;
4682 uint32_t oldName = 0;
4683 while ((isInside=(curEntry < set->numAttrs))
4684 && (oldName=entries[curEntry].map.name.ident) < newName) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004685 if (kDebugTableNoisy) {
4686 ALOGI("#%zu: Keeping existing attribute: 0x%08x\n",
4687 curEntry, entries[curEntry].map.name.ident);
4688 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004689 curEntry++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004690 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004691
4692 if ((!isInside) || oldName != newName) {
4693 // This is a new attribute... figure out what to do with it.
4694 if (set->numAttrs >= set->availAttrs) {
4695 // Need to alloc more memory...
4696 const size_t newAvail = set->availAttrs+N;
George Burgess IVe8efec52016-10-11 15:42:29 -07004697 void *oldSet = set;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004698 set = (bag_set*)realloc(set,
4699 sizeof(bag_set)
4700 + sizeof(bag_entry)*newAvail);
4701 if (set == NULL) {
George Burgess IVe8efec52016-10-11 15:42:29 -07004702 free(oldSet);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004703 return NO_MEMORY;
4704 }
4705 set->availAttrs = newAvail;
4706 entries = (bag_entry*)(set+1);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004707 if (kDebugTableNoisy) {
4708 ALOGI("Reallocated set %p, entries=%p, avail=%zu\n",
4709 set, entries, set->availAttrs);
4710 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004711 }
4712 if (isInside) {
4713 // Going in the middle, need to make space.
4714 memmove(entries+curEntry+1, entries+curEntry,
4715 sizeof(bag_entry)*(set->numAttrs-curEntry));
4716 set->numAttrs++;
4717 }
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004718 if (kDebugTableNoisy) {
4719 ALOGI("#%zu: Inserting new attribute: 0x%08x\n", curEntry, newName);
4720 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004721 } else {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004722 if (kDebugTableNoisy) {
4723 ALOGI("#%zu: Replacing existing attribute: 0x%08x\n", curEntry, oldName);
4724 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004725 }
4726
4727 bag_entry* cur = entries+curEntry;
4728
4729 cur->stringBlock = entry.package->header->index;
4730 cur->map.name.ident = newName;
4731 cur->map.value.copyFrom_dtoh(map->value);
4732 status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value);
4733 if (err != NO_ERROR) {
4734 ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data);
4735 return UNKNOWN_ERROR;
4736 }
4737
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004738 if (kDebugTableNoisy) {
4739 ALOGI("Setting entry #%zu %p: block=%zd, name=0x%08d, type=%d, data=0x%08x\n",
4740 curEntry, cur, cur->stringBlock, cur->map.name.ident,
4741 cur->map.value.dataType, cur->map.value.data);
4742 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004743
4744 // On to the next!
4745 curEntry++;
4746 pos++;
4747 const size_t size = dtohs(map->value.size);
4748 curOff += size + sizeof(*map)-sizeof(map->value);
George Burgess IVe8efec52016-10-11 15:42:29 -07004749 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004750
4751 if (curEntry > set->numAttrs) {
4752 set->numAttrs = curEntry;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004753 }
4754
4755 // And this is it...
4756 typeSet[e] = set;
4757 if (set) {
4758 if (outTypeSpecFlags != NULL) {
4759 *outTypeSpecFlags = set->typeSpecFlags;
4760 }
4761 *outBag = (bag_entry*)(set+1);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004762 if (kDebugTableNoisy) {
4763 ALOGI("Returning %zu attrs\n", set->numAttrs);
4764 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004765 return set->numAttrs;
4766 }
4767 return BAD_INDEX;
4768}
4769
4770void ResTable::setParameters(const ResTable_config* params)
4771{
Adam Lesinskiff5808d2016-02-23 17:49:53 -08004772 AutoMutex _lock(mLock);
4773 AutoMutex _lock2(mFilteredConfigLock);
4774
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004775 if (kDebugTableGetEntry) {
4776 ALOGI("Setting parameters: %s\n", params->toString().string());
4777 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004778 mParams = *params;
Adam Lesinskiff5808d2016-02-23 17:49:53 -08004779 for (size_t p = 0; p < mPackageGroups.size(); p++) {
4780 PackageGroup* packageGroup = mPackageGroups.editItemAt(p);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004781 if (kDebugTableNoisy) {
Adam Lesinskiff5808d2016-02-23 17:49:53 -08004782 ALOGI("CLEARING BAGS FOR GROUP %zu!", p);
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004783 }
Adam Lesinskiff5808d2016-02-23 17:49:53 -08004784 packageGroup->clearBagCache();
4785
4786 // Find which configurations match the set of parameters. This allows for a much
4787 // faster lookup in getEntry() if the set of values is narrowed down.
4788 for (size_t t = 0; t < packageGroup->types.size(); t++) {
4789 if (packageGroup->types[t].isEmpty()) {
4790 continue;
4791 }
4792
4793 TypeList& typeList = packageGroup->types.editItemAt(t);
4794
4795 // Retrieve the cache entry for this type.
4796 TypeCacheEntry& cacheEntry = packageGroup->typeCacheEntries.editItemAt(t);
4797
4798 for (size_t ts = 0; ts < typeList.size(); ts++) {
4799 Type* type = typeList.editItemAt(ts);
4800
4801 std::shared_ptr<Vector<const ResTable_type*>> newFilteredConfigs =
4802 std::make_shared<Vector<const ResTable_type*>>();
4803
4804 for (size_t ti = 0; ti < type->configs.size(); ti++) {
4805 ResTable_config config;
4806 config.copyFromDtoH(type->configs[ti]->config);
4807
4808 if (config.match(mParams)) {
4809 newFilteredConfigs->add(type->configs[ti]);
4810 }
4811 }
4812
4813 if (kDebugTableNoisy) {
4814 ALOGD("Updating pkg=%zu type=%zu with %zu filtered configs",
4815 p, t, newFilteredConfigs->size());
4816 }
4817
4818 cacheEntry.filteredConfigs.add(newFilteredConfigs);
4819 }
4820 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004821 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004822}
4823
4824void ResTable::getParameters(ResTable_config* params) const
4825{
4826 mLock.lock();
4827 *params = mParams;
4828 mLock.unlock();
4829}
4830
4831struct id_name_map {
4832 uint32_t id;
4833 size_t len;
4834 char16_t name[6];
4835};
4836
4837const static id_name_map ID_NAMES[] = {
4838 { ResTable_map::ATTR_TYPE, 5, { '^', 't', 'y', 'p', 'e' } },
4839 { ResTable_map::ATTR_L10N, 5, { '^', 'l', '1', '0', 'n' } },
4840 { ResTable_map::ATTR_MIN, 4, { '^', 'm', 'i', 'n' } },
4841 { ResTable_map::ATTR_MAX, 4, { '^', 'm', 'a', 'x' } },
4842 { ResTable_map::ATTR_OTHER, 6, { '^', 'o', 't', 'h', 'e', 'r' } },
4843 { ResTable_map::ATTR_ZERO, 5, { '^', 'z', 'e', 'r', 'o' } },
4844 { ResTable_map::ATTR_ONE, 4, { '^', 'o', 'n', 'e' } },
4845 { ResTable_map::ATTR_TWO, 4, { '^', 't', 'w', 'o' } },
4846 { ResTable_map::ATTR_FEW, 4, { '^', 'f', 'e', 'w' } },
4847 { ResTable_map::ATTR_MANY, 5, { '^', 'm', 'a', 'n', 'y' } },
4848};
4849
4850uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen,
4851 const char16_t* type, size_t typeLen,
4852 const char16_t* package,
4853 size_t packageLen,
4854 uint32_t* outTypeSpecFlags) const
4855{
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004856 if (kDebugTableSuperNoisy) {
4857 printf("Identifier for name: error=%d\n", mError);
4858 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004859
4860 // Check for internal resource identifier as the very first thing, so
4861 // that we will always find them even when there are no resources.
4862 if (name[0] == '^') {
4863 const int N = (sizeof(ID_NAMES)/sizeof(ID_NAMES[0]));
4864 size_t len;
4865 for (int i=0; i<N; i++) {
4866 const id_name_map* m = ID_NAMES + i;
4867 len = m->len;
4868 if (len != nameLen) {
4869 continue;
4870 }
4871 for (size_t j=1; j<len; j++) {
4872 if (m->name[j] != name[j]) {
4873 goto nope;
4874 }
4875 }
Dianne Hackborn426431a2011-06-09 11:29:08 -07004876 if (outTypeSpecFlags) {
4877 *outTypeSpecFlags = ResTable_typeSpec::SPEC_PUBLIC;
4878 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004879 return m->id;
4880nope:
4881 ;
4882 }
4883 if (nameLen > 7) {
4884 if (name[1] == 'i' && name[2] == 'n'
4885 && name[3] == 'd' && name[4] == 'e' && name[5] == 'x'
4886 && name[6] == '_') {
4887 int index = atoi(String8(name + 7, nameLen - 7).string());
4888 if (Res_CHECKID(index)) {
Steve Block8564c8d2012-01-05 23:22:43 +00004889 ALOGW("Array resource index: %d is too large.",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004890 index);
4891 return 0;
4892 }
Dianne Hackborn426431a2011-06-09 11:29:08 -07004893 if (outTypeSpecFlags) {
4894 *outTypeSpecFlags = ResTable_typeSpec::SPEC_PUBLIC;
4895 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004896 return Res_MAKEARRAY(index);
4897 }
4898 }
4899 return 0;
4900 }
4901
4902 if (mError != NO_ERROR) {
4903 return 0;
4904 }
4905
Dianne Hackborn426431a2011-06-09 11:29:08 -07004906 bool fakePublic = false;
4907
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004908 // Figure out the package and type we are looking in...
4909
4910 const char16_t* packageEnd = NULL;
4911 const char16_t* typeEnd = NULL;
4912 const char16_t* const nameEnd = name+nameLen;
4913 const char16_t* p = name;
4914 while (p < nameEnd) {
4915 if (*p == ':') packageEnd = p;
4916 else if (*p == '/') typeEnd = p;
4917 p++;
4918 }
Dianne Hackborn426431a2011-06-09 11:29:08 -07004919 if (*name == '@') {
4920 name++;
4921 if (*name == '*') {
4922 fakePublic = true;
4923 name++;
4924 }
4925 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004926 if (name >= nameEnd) {
4927 return 0;
4928 }
4929
4930 if (packageEnd) {
4931 package = name;
4932 packageLen = packageEnd-name;
4933 name = packageEnd+1;
4934 } else if (!package) {
4935 return 0;
4936 }
4937
4938 if (typeEnd) {
4939 type = name;
4940 typeLen = typeEnd-name;
4941 name = typeEnd+1;
4942 } else if (!type) {
4943 return 0;
4944 }
4945
4946 if (name >= nameEnd) {
4947 return 0;
4948 }
4949 nameLen = nameEnd-name;
4950
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004951 if (kDebugTableNoisy) {
4952 printf("Looking for identifier: type=%s, name=%s, package=%s\n",
4953 String8(type, typeLen).string(),
4954 String8(name, nameLen).string(),
4955 String8(package, packageLen).string());
4956 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004957
Adam Lesinski9b624c12014-11-19 17:49:26 -08004958 const String16 attr("attr");
4959 const String16 attrPrivate("^attr-private");
4960
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004961 const size_t NG = mPackageGroups.size();
4962 for (size_t ig=0; ig<NG; ig++) {
4963 const PackageGroup* group = mPackageGroups[ig];
4964
4965 if (strzcmp16(package, packageLen,
4966 group->name.string(), group->name.size())) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07004967 if (kDebugTableNoisy) {
4968 printf("Skipping package group: %s\n", String8(group->name).string());
4969 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004970 continue;
4971 }
4972
Adam Lesinskie60a87f2014-10-09 11:08:04 -07004973 const size_t packageCount = group->packages.size();
4974 for (size_t pi = 0; pi < packageCount; pi++) {
Adam Lesinski9b624c12014-11-19 17:49:26 -08004975 const char16_t* targetType = type;
4976 size_t targetTypeLen = typeLen;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004977
Adam Lesinski9b624c12014-11-19 17:49:26 -08004978 do {
4979 ssize_t ti = group->packages[pi]->typeStrings.indexOfString(
4980 targetType, targetTypeLen);
4981 if (ti < 0) {
Adam Lesinskie60a87f2014-10-09 11:08:04 -07004982 continue;
4983 }
4984
Adam Lesinski9b624c12014-11-19 17:49:26 -08004985 ti += group->packages[pi]->typeIdOffset;
Adam Lesinskie60a87f2014-10-09 11:08:04 -07004986
Adam Lesinski9b624c12014-11-19 17:49:26 -08004987 const uint32_t identifier = findEntry(group, ti, name, nameLen,
4988 outTypeSpecFlags);
4989 if (identifier != 0) {
4990 if (fakePublic && outTypeSpecFlags) {
4991 *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07004992 }
Adam Lesinski9b624c12014-11-19 17:49:26 -08004993 return identifier;
4994 }
4995 } while (strzcmp16(attr.string(), attr.size(), targetType, targetTypeLen) == 0
4996 && (targetType = attrPrivate.string())
4997 && (targetTypeLen = attrPrivate.size())
4998 );
4999 }
Adam Lesinski9b624c12014-11-19 17:49:26 -08005000 }
5001 return 0;
5002}
5003
5004uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const char16_t* name,
5005 size_t nameLen, uint32_t* outTypeSpecFlags) const {
5006 const TypeList& typeList = group->types[typeIndex];
5007 const size_t typeCount = typeList.size();
5008 for (size_t i = 0; i < typeCount; i++) {
5009 const Type* t = typeList[i];
5010 const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen);
5011 if (ei < 0) {
5012 continue;
5013 }
5014
5015 const size_t configCount = t->configs.size();
5016 for (size_t j = 0; j < configCount; j++) {
5017 const TypeVariant tv(t->configs[j]);
5018 for (TypeVariant::iterator iter = tv.beginEntries();
5019 iter != tv.endEntries();
5020 iter++) {
5021 const ResTable_entry* entry = *iter;
5022 if (entry == NULL) {
5023 continue;
5024 }
5025
5026 if (dtohl(entry->key.index) == (size_t) ei) {
5027 uint32_t resId = Res_MAKEID(group->id - 1, typeIndex, iter.index());
5028 if (outTypeSpecFlags) {
5029 Entry result;
5030 if (getEntry(group, typeIndex, iter.index(), NULL, &result) != NO_ERROR) {
5031 ALOGW("Failed to find spec flags for 0x%08x", resId);
5032 return 0;
5033 }
5034 *outTypeSpecFlags = result.specFlags;
5035 }
5036 return resId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005037 }
5038 }
5039 }
5040 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005041 return 0;
5042}
5043
Dan Albertf348c152014-09-08 18:28:00 -07005044bool ResTable::expandResourceRef(const char16_t* refStr, size_t refLen,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005045 String16* outPackage,
5046 String16* outType,
5047 String16* outName,
5048 const String16* defType,
5049 const String16* defPackage,
Dianne Hackborn426431a2011-06-09 11:29:08 -07005050 const char** outErrorMsg,
5051 bool* outPublicOnly)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005052{
5053 const char16_t* packageEnd = NULL;
5054 const char16_t* typeEnd = NULL;
5055 const char16_t* p = refStr;
5056 const char16_t* const end = p + refLen;
5057 while (p < end) {
5058 if (*p == ':') packageEnd = p;
5059 else if (*p == '/') {
5060 typeEnd = p;
5061 break;
5062 }
5063 p++;
5064 }
5065 p = refStr;
5066 if (*p == '@') p++;
5067
Dianne Hackborn426431a2011-06-09 11:29:08 -07005068 if (outPublicOnly != NULL) {
5069 *outPublicOnly = true;
5070 }
5071 if (*p == '*') {
5072 p++;
5073 if (outPublicOnly != NULL) {
5074 *outPublicOnly = false;
5075 }
5076 }
5077
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005078 if (packageEnd) {
5079 *outPackage = String16(p, packageEnd-p);
5080 p = packageEnd+1;
5081 } else {
5082 if (!defPackage) {
5083 if (outErrorMsg) {
5084 *outErrorMsg = "No resource package specified";
5085 }
5086 return false;
5087 }
5088 *outPackage = *defPackage;
5089 }
5090 if (typeEnd) {
5091 *outType = String16(p, typeEnd-p);
5092 p = typeEnd+1;
5093 } else {
5094 if (!defType) {
5095 if (outErrorMsg) {
5096 *outErrorMsg = "No resource type specified";
5097 }
5098 return false;
5099 }
5100 *outType = *defType;
5101 }
5102 *outName = String16(p, end-p);
Konstantin Lopyrevddcafcb2010-06-04 14:36:49 -07005103 if(**outPackage == 0) {
5104 if(outErrorMsg) {
5105 *outErrorMsg = "Resource package cannot be an empty string";
5106 }
5107 return false;
5108 }
5109 if(**outType == 0) {
5110 if(outErrorMsg) {
5111 *outErrorMsg = "Resource type cannot be an empty string";
5112 }
5113 return false;
5114 }
5115 if(**outName == 0) {
5116 if(outErrorMsg) {
5117 *outErrorMsg = "Resource id cannot be an empty string";
5118 }
5119 return false;
5120 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005121 return true;
5122}
5123
5124static uint32_t get_hex(char c, bool* outError)
5125{
5126 if (c >= '0' && c <= '9') {
5127 return c - '0';
5128 } else if (c >= 'a' && c <= 'f') {
5129 return c - 'a' + 0xa;
5130 } else if (c >= 'A' && c <= 'F') {
5131 return c - 'A' + 0xa;
5132 }
5133 *outError = true;
5134 return 0;
5135}
5136
5137struct unit_entry
5138{
5139 const char* name;
5140 size_t len;
5141 uint8_t type;
5142 uint32_t unit;
5143 float scale;
5144};
5145
5146static const unit_entry unitNames[] = {
5147 { "px", strlen("px"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PX, 1.0f },
5148 { "dip", strlen("dip"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f },
5149 { "dp", strlen("dp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f },
5150 { "sp", strlen("sp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_SP, 1.0f },
5151 { "pt", strlen("pt"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PT, 1.0f },
5152 { "in", strlen("in"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_IN, 1.0f },
5153 { "mm", strlen("mm"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_MM, 1.0f },
5154 { "%", strlen("%"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION, 1.0f/100 },
5155 { "%p", strlen("%p"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100 },
5156 { NULL, 0, 0, 0, 0 }
5157};
5158
5159static bool parse_unit(const char* str, Res_value* outValue,
5160 float* outScale, const char** outEnd)
5161{
5162 const char* end = str;
5163 while (*end != 0 && !isspace((unsigned char)*end)) {
5164 end++;
5165 }
5166 const size_t len = end-str;
5167
5168 const char* realEnd = end;
5169 while (*realEnd != 0 && isspace((unsigned char)*realEnd)) {
5170 realEnd++;
5171 }
5172 if (*realEnd != 0) {
5173 return false;
5174 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07005175
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005176 const unit_entry* cur = unitNames;
5177 while (cur->name) {
5178 if (len == cur->len && strncmp(cur->name, str, len) == 0) {
5179 outValue->dataType = cur->type;
5180 outValue->data = cur->unit << Res_value::COMPLEX_UNIT_SHIFT;
5181 *outScale = cur->scale;
5182 *outEnd = end;
5183 //printf("Found unit %s for %s\n", cur->name, str);
5184 return true;
5185 }
5186 cur++;
5187 }
5188
5189 return false;
5190}
5191
Dan Albert1b4f3162015-04-07 18:43:15 -07005192bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005193{
5194 while (len > 0 && isspace16(*s)) {
5195 s++;
5196 len--;
5197 }
5198
5199 if (len <= 0) {
5200 return false;
5201 }
5202
5203 size_t i = 0;
Dan Albert1b4f3162015-04-07 18:43:15 -07005204 int64_t val = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005205 bool neg = false;
5206
5207 if (*s == '-') {
5208 neg = true;
5209 i++;
5210 }
5211
5212 if (s[i] < '0' || s[i] > '9') {
5213 return false;
5214 }
5215
Dan Albert1b4f3162015-04-07 18:43:15 -07005216 static_assert(std::is_same<uint32_t, Res_value::data_type>::value,
5217 "Res_value::data_type has changed. The range checks in this "
5218 "function are no longer correct.");
5219
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005220 // Decimal or hex?
Dan Albert1b4f3162015-04-07 18:43:15 -07005221 bool isHex;
5222 if (len > 1 && s[i] == '0' && s[i+1] == 'x') {
5223 isHex = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005224 i += 2;
Dan Albert1b4f3162015-04-07 18:43:15 -07005225
5226 if (neg) {
5227 return false;
5228 }
5229
5230 if (i == len) {
5231 // Just u"0x"
5232 return false;
5233 }
5234
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005235 bool error = false;
5236 while (i < len && !error) {
5237 val = (val*16) + get_hex(s[i], &error);
5238 i++;
Dan Albert1b4f3162015-04-07 18:43:15 -07005239
5240 if (val > std::numeric_limits<uint32_t>::max()) {
5241 return false;
5242 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005243 }
5244 if (error) {
5245 return false;
5246 }
5247 } else {
Dan Albert1b4f3162015-04-07 18:43:15 -07005248 isHex = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005249 while (i < len) {
5250 if (s[i] < '0' || s[i] > '9') {
5251 return false;
5252 }
5253 val = (val*10) + s[i]-'0';
5254 i++;
Dan Albert1b4f3162015-04-07 18:43:15 -07005255
5256 if ((neg && -val < std::numeric_limits<int32_t>::min()) ||
5257 (!neg && val > std::numeric_limits<int32_t>::max())) {
5258 return false;
5259 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005260 }
5261 }
5262
5263 if (neg) val = -val;
5264
5265 while (i < len && isspace16(s[i])) {
5266 i++;
5267 }
5268
Dan Albert1b4f3162015-04-07 18:43:15 -07005269 if (i != len) {
5270 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005271 }
5272
Dan Albert1b4f3162015-04-07 18:43:15 -07005273 if (outValue) {
5274 outValue->dataType =
5275 isHex ? outValue->TYPE_INT_HEX : outValue->TYPE_INT_DEC;
5276 outValue->data = static_cast<Res_value::data_type>(val);
5277 }
5278 return true;
5279}
5280
5281bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue)
5282{
5283 return U16StringToInt(s, len, outValue);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005284}
5285
5286bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue)
5287{
5288 while (len > 0 && isspace16(*s)) {
5289 s++;
5290 len--;
5291 }
5292
5293 if (len <= 0) {
5294 return false;
5295 }
5296
5297 char buf[128];
5298 int i=0;
5299 while (len > 0 && *s != 0 && i < 126) {
5300 if (*s > 255) {
5301 return false;
5302 }
5303 buf[i++] = *s++;
5304 len--;
5305 }
5306
5307 if (len > 0) {
5308 return false;
5309 }
Torne (Richard Coles)46a807f2014-08-27 12:36:44 +01005310 if ((buf[0] < '0' || buf[0] > '9') && buf[0] != '.' && buf[0] != '-' && buf[0] != '+') {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005311 return false;
5312 }
5313
5314 buf[i] = 0;
5315 const char* end;
5316 float f = strtof(buf, (char**)&end);
5317
5318 if (*end != 0 && !isspace((unsigned char)*end)) {
5319 // Might be a unit...
5320 float scale;
5321 if (parse_unit(end, outValue, &scale, &end)) {
5322 f *= scale;
5323 const bool neg = f < 0;
5324 if (neg) f = -f;
5325 uint64_t bits = (uint64_t)(f*(1<<23)+.5f);
5326 uint32_t radix;
5327 uint32_t shift;
5328 if ((bits&0x7fffff) == 0) {
5329 // Always use 23p0 if there is no fraction, just to make
5330 // things easier to read.
5331 radix = Res_value::COMPLEX_RADIX_23p0;
5332 shift = 23;
5333 } else if ((bits&0xffffffffff800000LL) == 0) {
5334 // Magnitude is zero -- can fit in 0 bits of precision.
5335 radix = Res_value::COMPLEX_RADIX_0p23;
5336 shift = 0;
5337 } else if ((bits&0xffffffff80000000LL) == 0) {
5338 // Magnitude can fit in 8 bits of precision.
5339 radix = Res_value::COMPLEX_RADIX_8p15;
5340 shift = 8;
5341 } else if ((bits&0xffffff8000000000LL) == 0) {
5342 // Magnitude can fit in 16 bits of precision.
5343 radix = Res_value::COMPLEX_RADIX_16p7;
5344 shift = 16;
5345 } else {
5346 // Magnitude needs entire range, so no fractional part.
5347 radix = Res_value::COMPLEX_RADIX_23p0;
5348 shift = 23;
5349 }
5350 int32_t mantissa = (int32_t)(
5351 (bits>>shift) & Res_value::COMPLEX_MANTISSA_MASK);
5352 if (neg) {
5353 mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK;
5354 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07005355 outValue->data |=
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005356 (radix<<Res_value::COMPLEX_RADIX_SHIFT)
5357 | (mantissa<<Res_value::COMPLEX_MANTISSA_SHIFT);
5358 //printf("Input value: %f 0x%016Lx, mult: %f, radix: %d, shift: %d, final: 0x%08x\n",
5359 // f * (neg ? -1 : 1), bits, f*(1<<23),
5360 // radix, shift, outValue->data);
5361 return true;
5362 }
5363 return false;
5364 }
5365
5366 while (*end != 0 && isspace((unsigned char)*end)) {
5367 end++;
5368 }
5369
5370 if (*end == 0) {
5371 if (outValue) {
5372 outValue->dataType = outValue->TYPE_FLOAT;
5373 *(float*)(&outValue->data) = f;
5374 return true;
5375 }
5376 }
5377
5378 return false;
5379}
5380
5381bool ResTable::stringToValue(Res_value* outValue, String16* outString,
5382 const char16_t* s, size_t len,
5383 bool preserveSpaces, bool coerceType,
5384 uint32_t attrID,
5385 const String16* defType,
5386 const String16* defPackage,
5387 Accessor* accessor,
5388 void* accessorCookie,
5389 uint32_t attrType,
5390 bool enforcePrivate) const
5391{
5392 bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting();
5393 const char* errorMsg = NULL;
5394
5395 outValue->size = sizeof(Res_value);
5396 outValue->res0 = 0;
5397
5398 // First strip leading/trailing whitespace. Do this before handling
5399 // escapes, so they can be used to force whitespace into the string.
5400 if (!preserveSpaces) {
5401 while (len > 0 && isspace16(*s)) {
5402 s++;
5403 len--;
5404 }
5405 while (len > 0 && isspace16(s[len-1])) {
5406 len--;
5407 }
5408 // If the string ends with '\', then we keep the space after it.
5409 if (len > 0 && s[len-1] == '\\' && s[len] != 0) {
5410 len++;
5411 }
5412 }
5413
5414 //printf("Value for: %s\n", String8(s, len).string());
5415
5416 uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED;
5417 uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff;
5418 bool fromAccessor = false;
5419 if (attrID != 0 && !Res_INTERNALID(attrID)) {
5420 const ssize_t p = getResourcePackageIndex(attrID);
5421 const bag_entry* bag;
5422 ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
5423 //printf("For attr 0x%08x got bag of %d\n", attrID, cnt);
5424 if (cnt >= 0) {
5425 while (cnt > 0) {
5426 //printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data);
5427 switch (bag->map.name.ident) {
5428 case ResTable_map::ATTR_TYPE:
5429 attrType = bag->map.value.data;
5430 break;
5431 case ResTable_map::ATTR_MIN:
5432 attrMin = bag->map.value.data;
5433 break;
5434 case ResTable_map::ATTR_MAX:
5435 attrMax = bag->map.value.data;
5436 break;
5437 case ResTable_map::ATTR_L10N:
5438 l10nReq = bag->map.value.data;
5439 break;
5440 }
5441 bag++;
5442 cnt--;
5443 }
5444 unlockBag(bag);
5445 } else if (accessor && accessor->getAttributeType(attrID, &attrType)) {
5446 fromAccessor = true;
5447 if (attrType == ResTable_map::TYPE_ENUM
5448 || attrType == ResTable_map::TYPE_FLAGS
5449 || attrType == ResTable_map::TYPE_INTEGER) {
5450 accessor->getAttributeMin(attrID, &attrMin);
5451 accessor->getAttributeMax(attrID, &attrMax);
5452 }
5453 if (localizationSetting) {
5454 l10nReq = accessor->getAttributeL10N(attrID);
5455 }
5456 }
5457 }
5458
5459 const bool canStringCoerce =
5460 coerceType && (attrType&ResTable_map::TYPE_STRING) != 0;
5461
5462 if (*s == '@') {
5463 outValue->dataType = outValue->TYPE_REFERENCE;
5464
5465 // Note: we don't check attrType here because the reference can
5466 // be to any other type; we just need to count on the client making
5467 // sure the referenced type is correct.
Mark Salyzyn00adb862014-03-19 11:00:06 -07005468
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005469 //printf("Looking up ref: %s\n", String8(s, len).string());
5470
5471 // It's a reference!
5472 if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') {
Alan Viverettef2969402014-10-29 17:09:36 -07005473 // Special case @null as undefined. This will be converted by
5474 // AssetManager to TYPE_NULL with data DATA_NULL_UNDEFINED.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005475 outValue->data = 0;
5476 return true;
Alan Viverettef2969402014-10-29 17:09:36 -07005477 } else if (len == 6 && s[1]=='e' && s[2]=='m' && s[3]=='p' && s[4]=='t' && s[5]=='y') {
5478 // Special case @empty as explicitly defined empty value.
5479 outValue->dataType = Res_value::TYPE_NULL;
5480 outValue->data = Res_value::DATA_NULL_EMPTY;
5481 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005482 } else {
5483 bool createIfNotFound = false;
5484 const char16_t* resourceRefName;
5485 int resourceNameLen;
5486 if (len > 2 && s[1] == '+') {
5487 createIfNotFound = true;
5488 resourceRefName = s + 2;
5489 resourceNameLen = len - 2;
5490 } else if (len > 2 && s[1] == '*') {
5491 enforcePrivate = false;
5492 resourceRefName = s + 2;
5493 resourceNameLen = len - 2;
5494 } else {
5495 createIfNotFound = false;
5496 resourceRefName = s + 1;
5497 resourceNameLen = len - 1;
5498 }
5499 String16 package, type, name;
5500 if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name,
5501 defType, defPackage, &errorMsg)) {
5502 if (accessor != NULL) {
5503 accessor->reportError(accessorCookie, errorMsg);
5504 }
5505 return false;
5506 }
5507
5508 uint32_t specFlags = 0;
5509 uint32_t rid = identifierForName(name.string(), name.size(), type.string(),
5510 type.size(), package.string(), package.size(), &specFlags);
5511 if (rid != 0) {
5512 if (enforcePrivate) {
Adam Lesinski833f3cc2014-06-18 15:06:01 -07005513 if (accessor == NULL || accessor->getAssetsPackage() != package) {
5514 if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
5515 if (accessor != NULL) {
5516 accessor->reportError(accessorCookie, "Resource is not public.");
5517 }
5518 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005519 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005520 }
5521 }
Adam Lesinskide898ff2014-01-29 18:20:45 -08005522
5523 if (accessor) {
5524 rid = Res_MAKEID(
5525 accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
5526 Res_GETTYPE(rid), Res_GETENTRY(rid));
Andreas Gampe2204f0b2014-10-21 23:04:54 -07005527 if (kDebugTableNoisy) {
5528 ALOGI("Incl %s:%s/%s: 0x%08x\n",
5529 String8(package).string(), String8(type).string(),
5530 String8(name).string(), rid);
5531 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005532 }
Adam Lesinskide898ff2014-01-29 18:20:45 -08005533
5534 uint32_t packageId = Res_GETPACKAGE(rid) + 1;
5535 if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) {
5536 outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
5537 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005538 outValue->data = rid;
5539 return true;
5540 }
5541
5542 if (accessor) {
5543 uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name,
5544 createIfNotFound);
5545 if (rid != 0) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07005546 if (kDebugTableNoisy) {
5547 ALOGI("Pckg %s:%s/%s: 0x%08x\n",
5548 String8(package).string(), String8(type).string(),
5549 String8(name).string(), rid);
5550 }
Adam Lesinskide898ff2014-01-29 18:20:45 -08005551 uint32_t packageId = Res_GETPACKAGE(rid) + 1;
5552 if (packageId == 0x00) {
5553 outValue->data = rid;
5554 outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
5555 return true;
5556 } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) {
5557 // We accept packageId's generated as 0x01 in order to support
5558 // building the android system resources
5559 outValue->data = rid;
5560 return true;
5561 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005562 }
5563 }
5564 }
5565
5566 if (accessor != NULL) {
5567 accessor->reportError(accessorCookie, "No resource found that matches the given name");
5568 }
5569 return false;
5570 }
5571
5572 // if we got to here, and localization is required and it's not a reference,
5573 // complain and bail.
5574 if (l10nReq == ResTable_map::L10N_SUGGESTED) {
5575 if (localizationSetting) {
5576 if (accessor != NULL) {
5577 accessor->reportError(accessorCookie, "This attribute must be localized.");
5578 }
5579 }
5580 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07005581
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005582 if (*s == '#') {
5583 // It's a color! Convert to an integer of the form 0xaarrggbb.
5584 uint32_t color = 0;
5585 bool error = false;
5586 if (len == 4) {
5587 outValue->dataType = outValue->TYPE_INT_COLOR_RGB4;
5588 color |= 0xFF000000;
5589 color |= get_hex(s[1], &error) << 20;
5590 color |= get_hex(s[1], &error) << 16;
5591 color |= get_hex(s[2], &error) << 12;
5592 color |= get_hex(s[2], &error) << 8;
5593 color |= get_hex(s[3], &error) << 4;
5594 color |= get_hex(s[3], &error);
5595 } else if (len == 5) {
5596 outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4;
5597 color |= get_hex(s[1], &error) << 28;
5598 color |= get_hex(s[1], &error) << 24;
5599 color |= get_hex(s[2], &error) << 20;
5600 color |= get_hex(s[2], &error) << 16;
5601 color |= get_hex(s[3], &error) << 12;
5602 color |= get_hex(s[3], &error) << 8;
5603 color |= get_hex(s[4], &error) << 4;
5604 color |= get_hex(s[4], &error);
5605 } else if (len == 7) {
5606 outValue->dataType = outValue->TYPE_INT_COLOR_RGB8;
5607 color |= 0xFF000000;
5608 color |= get_hex(s[1], &error) << 20;
5609 color |= get_hex(s[2], &error) << 16;
5610 color |= get_hex(s[3], &error) << 12;
5611 color |= get_hex(s[4], &error) << 8;
5612 color |= get_hex(s[5], &error) << 4;
5613 color |= get_hex(s[6], &error);
5614 } else if (len == 9) {
5615 outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8;
5616 color |= get_hex(s[1], &error) << 28;
5617 color |= get_hex(s[2], &error) << 24;
5618 color |= get_hex(s[3], &error) << 20;
5619 color |= get_hex(s[4], &error) << 16;
5620 color |= get_hex(s[5], &error) << 12;
5621 color |= get_hex(s[6], &error) << 8;
5622 color |= get_hex(s[7], &error) << 4;
5623 color |= get_hex(s[8], &error);
5624 } else {
5625 error = true;
5626 }
5627 if (!error) {
5628 if ((attrType&ResTable_map::TYPE_COLOR) == 0) {
5629 if (!canStringCoerce) {
5630 if (accessor != NULL) {
5631 accessor->reportError(accessorCookie,
5632 "Color types not allowed");
5633 }
5634 return false;
5635 }
5636 } else {
5637 outValue->data = color;
5638 //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color);
5639 return true;
5640 }
5641 } else {
5642 if ((attrType&ResTable_map::TYPE_COLOR) != 0) {
5643 if (accessor != NULL) {
5644 accessor->reportError(accessorCookie, "Color value not valid --"
5645 " must be #rgb, #argb, #rrggbb, or #aarrggbb");
5646 }
5647 #if 0
5648 fprintf(stderr, "%s: Color ID %s value %s is not valid\n",
5649 "Resource File", //(const char*)in->getPrintableSource(),
5650 String8(*curTag).string(),
5651 String8(s, len).string());
5652 #endif
5653 return false;
5654 }
5655 }
5656 }
5657
5658 if (*s == '?') {
5659 outValue->dataType = outValue->TYPE_ATTRIBUTE;
5660
5661 // Note: we don't check attrType here because the reference can
5662 // be to any other type; we just need to count on the client making
5663 // sure the referenced type is correct.
5664
5665 //printf("Looking up attr: %s\n", String8(s, len).string());
5666
5667 static const String16 attr16("attr");
5668 String16 package, type, name;
5669 if (!expandResourceRef(s+1, len-1, &package, &type, &name,
5670 &attr16, defPackage, &errorMsg)) {
5671 if (accessor != NULL) {
5672 accessor->reportError(accessorCookie, errorMsg);
5673 }
5674 return false;
5675 }
5676
5677 //printf("Pkg: %s, Type: %s, Name: %s\n",
5678 // String8(package).string(), String8(type).string(),
5679 // String8(name).string());
5680 uint32_t specFlags = 0;
Mark Salyzyn00adb862014-03-19 11:00:06 -07005681 uint32_t rid =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005682 identifierForName(name.string(), name.size(),
5683 type.string(), type.size(),
5684 package.string(), package.size(), &specFlags);
5685 if (rid != 0) {
5686 if (enforcePrivate) {
5687 if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
5688 if (accessor != NULL) {
5689 accessor->reportError(accessorCookie, "Attribute is not public.");
5690 }
5691 return false;
5692 }
5693 }
Adam Lesinski8ac51d12016-05-10 10:01:12 -07005694
5695 if (accessor) {
5696 rid = Res_MAKEID(
5697 accessor->getRemappedPackage(Res_GETPACKAGE(rid)),
5698 Res_GETTYPE(rid), Res_GETENTRY(rid));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005699 }
Adam Lesinski8ac51d12016-05-10 10:01:12 -07005700
5701 uint32_t packageId = Res_GETPACKAGE(rid) + 1;
5702 if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) {
5703 outValue->dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE;
5704 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005705 outValue->data = rid;
5706 return true;
5707 }
5708
5709 if (accessor) {
5710 uint32_t rid = accessor->getCustomResource(package, type, name);
5711 if (rid != 0) {
Adam Lesinski8ac51d12016-05-10 10:01:12 -07005712 uint32_t packageId = Res_GETPACKAGE(rid) + 1;
5713 if (packageId == 0x00) {
5714 outValue->data = rid;
5715 outValue->dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE;
5716 return true;
5717 } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) {
5718 // We accept packageId's generated as 0x01 in order to support
5719 // building the android system resources
5720 outValue->data = rid;
5721 return true;
5722 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005723 }
5724 }
5725
5726 if (accessor != NULL) {
5727 accessor->reportError(accessorCookie, "No resource found that matches the given name");
5728 }
5729 return false;
5730 }
5731
5732 if (stringToInt(s, len, outValue)) {
5733 if ((attrType&ResTable_map::TYPE_INTEGER) == 0) {
5734 // If this type does not allow integers, but does allow floats,
5735 // fall through on this error case because the float type should
5736 // be able to accept any integer value.
5737 if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) {
5738 if (accessor != NULL) {
5739 accessor->reportError(accessorCookie, "Integer types not allowed");
5740 }
5741 return false;
5742 }
5743 } else {
5744 if (((int32_t)outValue->data) < ((int32_t)attrMin)
5745 || ((int32_t)outValue->data) > ((int32_t)attrMax)) {
5746 if (accessor != NULL) {
5747 accessor->reportError(accessorCookie, "Integer value out of range");
5748 }
5749 return false;
5750 }
5751 return true;
5752 }
5753 }
5754
5755 if (stringToFloat(s, len, outValue)) {
5756 if (outValue->dataType == Res_value::TYPE_DIMENSION) {
5757 if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) {
5758 return true;
5759 }
5760 if (!canStringCoerce) {
5761 if (accessor != NULL) {
5762 accessor->reportError(accessorCookie, "Dimension types not allowed");
5763 }
5764 return false;
5765 }
5766 } else if (outValue->dataType == Res_value::TYPE_FRACTION) {
5767 if ((attrType&ResTable_map::TYPE_FRACTION) != 0) {
5768 return true;
5769 }
5770 if (!canStringCoerce) {
5771 if (accessor != NULL) {
5772 accessor->reportError(accessorCookie, "Fraction types not allowed");
5773 }
5774 return false;
5775 }
5776 } else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) {
5777 if (!canStringCoerce) {
5778 if (accessor != NULL) {
5779 accessor->reportError(accessorCookie, "Float types not allowed");
5780 }
5781 return false;
5782 }
5783 } else {
5784 return true;
5785 }
5786 }
5787
5788 if (len == 4) {
5789 if ((s[0] == 't' || s[0] == 'T') &&
5790 (s[1] == 'r' || s[1] == 'R') &&
5791 (s[2] == 'u' || s[2] == 'U') &&
5792 (s[3] == 'e' || s[3] == 'E')) {
5793 if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) {
5794 if (!canStringCoerce) {
5795 if (accessor != NULL) {
5796 accessor->reportError(accessorCookie, "Boolean types not allowed");
5797 }
5798 return false;
5799 }
5800 } else {
5801 outValue->dataType = outValue->TYPE_INT_BOOLEAN;
5802 outValue->data = (uint32_t)-1;
5803 return true;
5804 }
5805 }
5806 }
5807
5808 if (len == 5) {
5809 if ((s[0] == 'f' || s[0] == 'F') &&
5810 (s[1] == 'a' || s[1] == 'A') &&
5811 (s[2] == 'l' || s[2] == 'L') &&
5812 (s[3] == 's' || s[3] == 'S') &&
5813 (s[4] == 'e' || s[4] == 'E')) {
5814 if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) {
5815 if (!canStringCoerce) {
5816 if (accessor != NULL) {
5817 accessor->reportError(accessorCookie, "Boolean types not allowed");
5818 }
5819 return false;
5820 }
5821 } else {
5822 outValue->dataType = outValue->TYPE_INT_BOOLEAN;
5823 outValue->data = 0;
5824 return true;
5825 }
5826 }
5827 }
5828
5829 if ((attrType&ResTable_map::TYPE_ENUM) != 0) {
5830 const ssize_t p = getResourcePackageIndex(attrID);
5831 const bag_entry* bag;
5832 ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
5833 //printf("Got %d for enum\n", cnt);
5834 if (cnt >= 0) {
5835 resource_name rname;
5836 while (cnt > 0) {
5837 if (!Res_INTERNALID(bag->map.name.ident)) {
5838 //printf("Trying attr #%08x\n", bag->map.name.ident);
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07005839 if (getResourceName(bag->map.name.ident, false, &rname)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005840 #if 0
5841 printf("Matching %s against %s (0x%08x)\n",
5842 String8(s, len).string(),
5843 String8(rname.name, rname.nameLen).string(),
5844 bag->map.name.ident);
5845 #endif
5846 if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) {
5847 outValue->dataType = bag->map.value.dataType;
5848 outValue->data = bag->map.value.data;
5849 unlockBag(bag);
5850 return true;
5851 }
5852 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07005853
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005854 }
5855 bag++;
5856 cnt--;
5857 }
5858 unlockBag(bag);
5859 }
5860
5861 if (fromAccessor) {
5862 if (accessor->getAttributeEnum(attrID, s, len, outValue)) {
5863 return true;
5864 }
5865 }
5866 }
5867
5868 if ((attrType&ResTable_map::TYPE_FLAGS) != 0) {
5869 const ssize_t p = getResourcePackageIndex(attrID);
5870 const bag_entry* bag;
5871 ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1;
5872 //printf("Got %d for flags\n", cnt);
5873 if (cnt >= 0) {
5874 bool failed = false;
5875 resource_name rname;
5876 outValue->dataType = Res_value::TYPE_INT_HEX;
5877 outValue->data = 0;
5878 const char16_t* end = s + len;
5879 const char16_t* pos = s;
5880 while (pos < end && !failed) {
5881 const char16_t* start = pos;
The Android Open Source Project4df24232009-03-05 14:34:35 -08005882 pos++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005883 while (pos < end && *pos != '|') {
5884 pos++;
5885 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08005886 //printf("Looking for: %s\n", String8(start, pos-start).string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005887 const bag_entry* bagi = bag;
The Android Open Source Project4df24232009-03-05 14:34:35 -08005888 ssize_t i;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005889 for (i=0; i<cnt; i++, bagi++) {
5890 if (!Res_INTERNALID(bagi->map.name.ident)) {
5891 //printf("Trying attr #%08x\n", bagi->map.name.ident);
Dianne Hackbornd45c68d2013-07-31 12:14:24 -07005892 if (getResourceName(bagi->map.name.ident, false, &rname)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005893 #if 0
5894 printf("Matching %s against %s (0x%08x)\n",
5895 String8(start,pos-start).string(),
5896 String8(rname.name, rname.nameLen).string(),
5897 bagi->map.name.ident);
5898 #endif
5899 if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) {
5900 outValue->data |= bagi->map.value.data;
5901 break;
5902 }
5903 }
5904 }
5905 }
5906 if (i >= cnt) {
5907 // Didn't find this flag identifier.
5908 failed = true;
5909 }
5910 if (pos < end) {
5911 pos++;
5912 }
5913 }
5914 unlockBag(bag);
5915 if (!failed) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08005916 //printf("Final flag value: 0x%lx\n", outValue->data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005917 return true;
5918 }
5919 }
5920
5921
5922 if (fromAccessor) {
5923 if (accessor->getAttributeFlags(attrID, s, len, outValue)) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08005924 //printf("Final flag value: 0x%lx\n", outValue->data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005925 return true;
5926 }
5927 }
5928 }
5929
5930 if ((attrType&ResTable_map::TYPE_STRING) == 0) {
5931 if (accessor != NULL) {
5932 accessor->reportError(accessorCookie, "String types not allowed");
5933 }
5934 return false;
5935 }
5936
5937 // Generic string handling...
5938 outValue->dataType = outValue->TYPE_STRING;
5939 if (outString) {
5940 bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg);
5941 if (accessor != NULL) {
5942 accessor->reportError(accessorCookie, errorMsg);
5943 }
5944 return failed;
5945 }
5946
5947 return true;
5948}
5949
5950bool ResTable::collectString(String16* outString,
5951 const char16_t* s, size_t len,
5952 bool preserveSpaces,
5953 const char** outErrorMsg,
5954 bool append)
5955{
5956 String16 tmp;
5957
5958 char quoted = 0;
5959 const char16_t* p = s;
5960 while (p < (s+len)) {
5961 while (p < (s+len)) {
5962 const char16_t c = *p;
5963 if (c == '\\') {
5964 break;
5965 }
5966 if (!preserveSpaces) {
5967 if (quoted == 0 && isspace16(c)
5968 && (c != ' ' || isspace16(*(p+1)))) {
5969 break;
5970 }
5971 if (c == '"' && (quoted == 0 || quoted == '"')) {
5972 break;
5973 }
5974 if (c == '\'' && (quoted == 0 || quoted == '\'')) {
Eric Fischerc87d2522009-09-01 15:20:30 -07005975 /*
5976 * In practice, when people write ' instead of \'
5977 * in a string, they are doing it by accident
5978 * instead of really meaning to use ' as a quoting
5979 * character. Warn them so they don't lose it.
5980 */
5981 if (outErrorMsg) {
5982 *outErrorMsg = "Apostrophe not preceded by \\";
5983 }
5984 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005985 }
5986 }
5987 p++;
5988 }
5989 if (p < (s+len)) {
5990 if (p > s) {
5991 tmp.append(String16(s, p-s));
5992 }
5993 if (!preserveSpaces && (*p == '"' || *p == '\'')) {
5994 if (quoted == 0) {
5995 quoted = *p;
5996 } else {
5997 quoted = 0;
5998 }
5999 p++;
6000 } else if (!preserveSpaces && isspace16(*p)) {
6001 // Space outside of a quote -- consume all spaces and
6002 // leave a single plain space char.
6003 tmp.append(String16(" "));
6004 p++;
6005 while (p < (s+len) && isspace16(*p)) {
6006 p++;
6007 }
6008 } else if (*p == '\\') {
6009 p++;
6010 if (p < (s+len)) {
6011 switch (*p) {
6012 case 't':
6013 tmp.append(String16("\t"));
6014 break;
6015 case 'n':
6016 tmp.append(String16("\n"));
6017 break;
6018 case '#':
6019 tmp.append(String16("#"));
6020 break;
6021 case '@':
6022 tmp.append(String16("@"));
6023 break;
6024 case '?':
6025 tmp.append(String16("?"));
6026 break;
6027 case '"':
6028 tmp.append(String16("\""));
6029 break;
6030 case '\'':
6031 tmp.append(String16("'"));
6032 break;
6033 case '\\':
6034 tmp.append(String16("\\"));
6035 break;
6036 case 'u':
6037 {
6038 char16_t chr = 0;
6039 int i = 0;
6040 while (i < 4 && p[1] != 0) {
6041 p++;
6042 i++;
6043 int c;
6044 if (*p >= '0' && *p <= '9') {
6045 c = *p - '0';
6046 } else if (*p >= 'a' && *p <= 'f') {
6047 c = *p - 'a' + 10;
6048 } else if (*p >= 'A' && *p <= 'F') {
6049 c = *p - 'A' + 10;
6050 } else {
6051 if (outErrorMsg) {
6052 *outErrorMsg = "Bad character in \\u unicode escape sequence";
6053 }
6054 return false;
6055 }
6056 chr = (chr<<4) | c;
6057 }
6058 tmp.append(String16(&chr, 1));
6059 } break;
6060 default:
6061 // ignore unknown escape chars.
6062 break;
6063 }
6064 p++;
6065 }
6066 }
6067 len -= (p-s);
6068 s = p;
6069 }
6070 }
6071
6072 if (tmp.size() != 0) {
6073 if (len > 0) {
6074 tmp.append(String16(s, len));
6075 }
6076 if (append) {
6077 outString->append(tmp);
6078 } else {
6079 outString->setTo(tmp);
6080 }
6081 } else {
6082 if (append) {
6083 outString->append(String16(s, len));
6084 } else {
6085 outString->setTo(s, len);
6086 }
6087 }
6088
6089 return true;
6090}
6091
6092size_t ResTable::getBasePackageCount() const
6093{
6094 if (mError != NO_ERROR) {
6095 return 0;
6096 }
6097 return mPackageGroups.size();
6098}
6099
Adam Lesinskide898ff2014-01-29 18:20:45 -08006100const String16 ResTable::getBasePackageName(size_t idx) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006101{
6102 if (mError != NO_ERROR) {
Adam Lesinskide898ff2014-01-29 18:20:45 -08006103 return String16();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006104 }
6105 LOG_FATAL_IF(idx >= mPackageGroups.size(),
6106 "Requested package index %d past package count %d",
6107 (int)idx, (int)mPackageGroups.size());
Adam Lesinskide898ff2014-01-29 18:20:45 -08006108 return mPackageGroups[idx]->name;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006109}
6110
6111uint32_t ResTable::getBasePackageId(size_t idx) const
6112{
6113 if (mError != NO_ERROR) {
6114 return 0;
6115 }
6116 LOG_FATAL_IF(idx >= mPackageGroups.size(),
6117 "Requested package index %d past package count %d",
6118 (int)idx, (int)mPackageGroups.size());
6119 return mPackageGroups[idx]->id;
6120}
6121
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006122uint32_t ResTable::getLastTypeIdForPackage(size_t idx) const
6123{
6124 if (mError != NO_ERROR) {
6125 return 0;
6126 }
6127 LOG_FATAL_IF(idx >= mPackageGroups.size(),
6128 "Requested package index %d past package count %d",
6129 (int)idx, (int)mPackageGroups.size());
6130 const PackageGroup* const group = mPackageGroups[idx];
6131 return group->largestTypeId;
6132}
6133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006134size_t ResTable::getTableCount() const
6135{
6136 return mHeaders.size();
6137}
6138
6139const ResStringPool* ResTable::getTableStringBlock(size_t index) const
6140{
6141 return &mHeaders[index]->values;
6142}
6143
Narayan Kamath7c4887f2014-01-27 17:32:37 +00006144int32_t ResTable::getTableCookie(size_t index) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006145{
6146 return mHeaders[index]->cookie;
6147}
6148
Adam Lesinskide898ff2014-01-29 18:20:45 -08006149const DynamicRefTable* ResTable::getDynamicRefTableForCookie(int32_t cookie) const
6150{
6151 const size_t N = mPackageGroups.size();
6152 for (size_t i = 0; i < N; i++) {
6153 const PackageGroup* pg = mPackageGroups[i];
6154 size_t M = pg->packages.size();
6155 for (size_t j = 0; j < M; j++) {
6156 if (pg->packages[j]->header->cookie == cookie) {
6157 return &pg->dynamicRefTable;
6158 }
6159 }
6160 }
6161 return NULL;
6162}
6163
Adam Lesinskib7e1ce02016-04-11 20:03:01 -07006164static bool compareResTableConfig(const ResTable_config& a, const ResTable_config& b) {
6165 return a.compare(b) < 0;
6166}
6167
Adam Lesinski76da37e2016-05-19 18:25:28 -07006168template <typename Func>
6169void ResTable::forEachConfiguration(bool ignoreMipmap, bool ignoreAndroidPackage,
6170 bool includeSystemConfigs, const Func& f) const {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006171 const size_t packageCount = mPackageGroups.size();
Adam Lesinski76da37e2016-05-19 18:25:28 -07006172 const String16 android("android");
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006173 for (size_t i = 0; i < packageCount; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006174 const PackageGroup* packageGroup = mPackageGroups[i];
Filip Gruszczynski23493322015-07-29 17:02:59 -07006175 if (ignoreAndroidPackage && android == packageGroup->name) {
6176 continue;
6177 }
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08006178 if (!includeSystemConfigs && packageGroup->isSystemAsset) {
6179 continue;
6180 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006181 const size_t typeCount = packageGroup->types.size();
6182 for (size_t j = 0; j < typeCount; j++) {
6183 const TypeList& typeList = packageGroup->types[j];
6184 const size_t numTypes = typeList.size();
6185 for (size_t k = 0; k < numTypes; k++) {
6186 const Type* type = typeList[k];
Adam Lesinski42eea272015-01-15 17:01:39 -08006187 const ResStringPool& typeStrings = type->package->typeStrings;
6188 if (ignoreMipmap && typeStrings.string8ObjectAt(
6189 type->typeSpec->id - 1) == "mipmap") {
6190 continue;
6191 }
6192
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006193 const size_t numConfigs = type->configs.size();
6194 for (size_t m = 0; m < numConfigs; m++) {
6195 const ResTable_type* config = type->configs[m];
Narayan Kamath788fa412014-01-21 15:32:36 +00006196 ResTable_config cfg;
6197 memset(&cfg, 0, sizeof(ResTable_config));
6198 cfg.copyFromDtoH(config->config);
Adam Lesinskib7e1ce02016-04-11 20:03:01 -07006199
Adam Lesinski76da37e2016-05-19 18:25:28 -07006200 f(cfg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006201 }
6202 }
6203 }
6204 }
6205}
6206
Adam Lesinski76da37e2016-05-19 18:25:28 -07006207void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap,
6208 bool ignoreAndroidPackage, bool includeSystemConfigs) const {
6209 auto func = [&](const ResTable_config& cfg) {
6210 const auto beginIter = configs->begin();
6211 const auto endIter = configs->end();
6212
6213 auto iter = std::lower_bound(beginIter, endIter, cfg, compareResTableConfig);
6214 if (iter == endIter || iter->compare(cfg) != 0) {
6215 configs->insertAt(cfg, std::distance(beginIter, iter));
6216 }
6217 };
6218 forEachConfiguration(ignoreMipmap, ignoreAndroidPackage, includeSystemConfigs, func);
6219}
6220
Adam Lesinskib7e1ce02016-04-11 20:03:01 -07006221static bool compareString8AndCString(const String8& str, const char* cStr) {
6222 return strcmp(str.string(), cStr) < 0;
6223}
6224
Roozbeh Pournader7e5f96f2016-06-13 18:10:49 -07006225void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales,
6226 bool mergeEquivalentLangs) const {
Narayan Kamath48620f12014-01-20 13:57:11 +00006227 char locale[RESTABLE_MAX_LOCALE_LEN];
Adam Lesinskib7e1ce02016-04-11 20:03:01 -07006228
Adam Lesinski76da37e2016-05-19 18:25:28 -07006229 forEachConfiguration(false, false, includeSystemLocales, [&](const ResTable_config& cfg) {
Adam Lesinskifa2fc0b2017-05-11 12:15:26 -07006230 cfg.getBcp47Locale(locale, mergeEquivalentLangs /* canonicalize if merging */);
Adam Lesinski76da37e2016-05-19 18:25:28 -07006231
Adam Lesinskifa2fc0b2017-05-11 12:15:26 -07006232 const auto beginIter = locales->begin();
6233 const auto endIter = locales->end();
Adam Lesinski76da37e2016-05-19 18:25:28 -07006234
Adam Lesinskifa2fc0b2017-05-11 12:15:26 -07006235 auto iter = std::lower_bound(beginIter, endIter, locale, compareString8AndCString);
6236 if (iter == endIter || strcmp(iter->string(), locale) != 0) {
6237 locales->insertAt(String8(locale), std::distance(beginIter, iter));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006238 }
Adam Lesinski76da37e2016-05-19 18:25:28 -07006239 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006240}
6241
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006242StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index)
6243 : mPool(pool), mIndex(index) {}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006244
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006245const char* StringPoolRef::string8(size_t* outLen) const {
6246 if (mPool != NULL) {
6247 return mPool->string8At(mIndex, outLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006248 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006249 if (outLen != NULL) {
6250 *outLen = 0;
6251 }
6252 return NULL;
6253}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006254
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006255const char16_t* StringPoolRef::string16(size_t* outLen) const {
6256 if (mPool != NULL) {
6257 return mPool->stringAt(mIndex, outLen);
6258 }
6259 if (outLen != NULL) {
6260 *outLen = 0;
6261 }
6262 return NULL;
6263}
6264
Adam Lesinski82a2dd82014-09-17 18:34:15 -07006265bool ResTable::getResourceFlags(uint32_t resID, uint32_t* outFlags) const {
6266 if (mError != NO_ERROR) {
6267 return false;
6268 }
6269
6270 const ssize_t p = getResourcePackageIndex(resID);
6271 const int t = Res_GETTYPE(resID);
6272 const int e = Res_GETENTRY(resID);
6273
6274 if (p < 0) {
6275 if (Res_GETPACKAGE(resID)+1 == 0) {
6276 ALOGW("No package identifier when getting flags for resource number 0x%08x", resID);
6277 } else {
6278 ALOGW("No known package when getting flags for resource number 0x%08x", resID);
6279 }
6280 return false;
6281 }
6282 if (t < 0) {
6283 ALOGW("No type identifier when getting flags for resource number 0x%08x", resID);
6284 return false;
6285 }
6286
6287 const PackageGroup* const grp = mPackageGroups[p];
6288 if (grp == NULL) {
6289 ALOGW("Bad identifier when getting flags for resource number 0x%08x", resID);
6290 return false;
6291 }
6292
6293 Entry entry;
6294 status_t err = getEntry(grp, t, e, NULL, &entry);
6295 if (err != NO_ERROR) {
6296 return false;
6297 }
6298
6299 *outFlags = entry.specFlags;
6300 return true;
6301}
6302
Todd Kennedy32512992018-04-25 16:45:59 -07006303bool ResTable::isPackageDynamic(uint8_t packageID) const {
6304 if (mError != NO_ERROR) {
6305 return false;
6306 }
6307 if (packageID == 0) {
6308 ALOGW("Invalid package number 0x%08x", packageID);
6309 return false;
6310 }
6311
6312 const ssize_t p = getResourcePackageIndexFromPackage(packageID);
6313
6314 if (p < 0) {
6315 ALOGW("Unknown package number 0x%08x", packageID);
6316 return false;
6317 }
6318
6319 const PackageGroup* const grp = mPackageGroups[p];
6320 if (grp == NULL) {
6321 ALOGW("Bad identifier for package number 0x%08x", packageID);
6322 return false;
6323 }
6324
6325 return grp->isDynamic;
6326}
6327
6328bool ResTable::isResourceDynamic(uint32_t resID) const {
6329 if (mError != NO_ERROR) {
6330 return false;
6331 }
6332
6333 const ssize_t p = getResourcePackageIndex(resID);
6334 const int t = Res_GETTYPE(resID);
6335 const int e = Res_GETENTRY(resID);
6336
6337 if (p < 0) {
6338 if (Res_GETPACKAGE(resID)+1 == 0) {
6339 ALOGW("No package identifier for resource number 0x%08x", resID);
6340 } else {
6341 ALOGW("No known package for resource number 0x%08x", resID);
6342 }
6343 return false;
6344 }
6345 if (t < 0) {
6346 ALOGW("No type identifier for resource number 0x%08x", resID);
6347 return false;
6348 }
6349
6350 const PackageGroup* const grp = mPackageGroups[p];
6351 if (grp == NULL) {
6352 ALOGW("Bad identifier for resource number 0x%08x", resID);
6353 return false;
6354 }
6355
6356 Entry entry;
6357 status_t err = getEntry(grp, t, e, NULL, &entry);
6358 if (err != NO_ERROR) {
6359 return false;
6360 }
6361
6362 return grp->isDynamic;
6363}
6364
Adam Lesinskic8f71aa2017-02-08 07:03:50 -08006365static bool keyCompare(const ResTable_sparseTypeEntry& entry , uint16_t entryIdx) {
6366 return dtohs(entry.idx) < entryIdx;
6367}
6368
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006369status_t ResTable::getEntry(
6370 const PackageGroup* packageGroup, int typeIndex, int entryIndex,
6371 const ResTable_config* config,
6372 Entry* outEntry) const
6373{
6374 const TypeList& typeList = packageGroup->types[typeIndex];
6375 if (typeList.isEmpty()) {
6376 ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006377 return BAD_TYPE;
6378 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07006379
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006380 const ResTable_type* bestType = NULL;
6381 uint32_t bestOffset = ResTable_type::NO_ENTRY;
6382 const Package* bestPackage = NULL;
6383 uint32_t specFlags = 0;
6384 uint8_t actualTypeIndex = typeIndex;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006385 ResTable_config bestConfig;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006386 memset(&bestConfig, 0, sizeof(bestConfig));
Mark Salyzyn00adb862014-03-19 11:00:06 -07006387
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006388 // Iterate over the Types of each package.
6389 const size_t typeCount = typeList.size();
6390 for (size_t i = 0; i < typeCount; i++) {
6391 const Type* const typeSpec = typeList[i];
Mark Salyzyn00adb862014-03-19 11:00:06 -07006392
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006393 int realEntryIndex = entryIndex;
6394 int realTypeIndex = typeIndex;
6395 bool currentTypeIsOverlay = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006396
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006397 // Runtime overlay packages provide a mapping of app resource
6398 // ID to package resource ID.
6399 if (typeSpec->idmapEntries.hasEntries()) {
6400 uint16_t overlayEntryIndex;
6401 if (typeSpec->idmapEntries.lookup(entryIndex, &overlayEntryIndex) != NO_ERROR) {
6402 // No such mapping exists
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006403 continue;
6404 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006405 realEntryIndex = overlayEntryIndex;
6406 realTypeIndex = typeSpec->idmapEntries.overlayTypeId() - 1;
6407 currentTypeIsOverlay = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006408 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07006409
Adam Lesinskic8f71aa2017-02-08 07:03:50 -08006410 // Check that the entry idx is within range of the declared entry count (ResTable_typeSpec).
6411 // Particular types (ResTable_type) may be encoded with sparse entries, and so their
6412 // entryCount do not need to match.
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006413 if (static_cast<size_t>(realEntryIndex) >= typeSpec->entryCount) {
6414 ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)",
6415 Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex),
6416 entryIndex, static_cast<int>(typeSpec->entryCount));
6417 // We should normally abort here, but some legacy apps declare
6418 // resources in the 'android' package (old bug in AAPT).
6419 continue;
6420 }
6421
6422 // Aggregate all the flags for each package that defines this entry.
6423 if (typeSpec->typeSpecFlags != NULL) {
6424 specFlags |= dtohl(typeSpec->typeSpecFlags[realEntryIndex]);
6425 } else {
6426 specFlags = -1;
6427 }
6428
Adam Lesinskiff5808d2016-02-23 17:49:53 -08006429 const Vector<const ResTable_type*>* candidateConfigs = &typeSpec->configs;
6430
6431 std::shared_ptr<Vector<const ResTable_type*>> filteredConfigs;
6432 if (config && memcmp(&mParams, config, sizeof(mParams)) == 0) {
6433 // Grab the lock first so we can safely get the current filtered list.
6434 AutoMutex _lock(mFilteredConfigLock);
6435
6436 // This configuration is equal to the one we have previously cached for,
6437 // so use the filtered configs.
6438
6439 const TypeCacheEntry& cacheEntry = packageGroup->typeCacheEntries[typeIndex];
6440 if (i < cacheEntry.filteredConfigs.size()) {
6441 if (cacheEntry.filteredConfigs[i]) {
6442 // Grab a reference to the shared_ptr so it doesn't get destroyed while
6443 // going through this list.
6444 filteredConfigs = cacheEntry.filteredConfigs[i];
6445
6446 // Use this filtered list.
6447 candidateConfigs = filteredConfigs.get();
6448 }
6449 }
6450 }
6451
6452 const size_t numConfigs = candidateConfigs->size();
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006453 for (size_t c = 0; c < numConfigs; c++) {
Adam Lesinskiff5808d2016-02-23 17:49:53 -08006454 const ResTable_type* const thisType = candidateConfigs->itemAt(c);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006455 if (thisType == NULL) {
6456 continue;
6457 }
6458
6459 ResTable_config thisConfig;
6460 thisConfig.copyFromDtoH(thisType->config);
6461
6462 // Check to make sure this one is valid for the current parameters.
6463 if (config != NULL && !thisConfig.match(*config)) {
6464 continue;
6465 }
6466
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006467 const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
6468 reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));
6469
Adam Lesinskic8f71aa2017-02-08 07:03:50 -08006470 uint32_t thisOffset;
6471
6472 // Check if there is the desired entry in this type.
6473 if (thisType->flags & ResTable_type::FLAG_SPARSE) {
6474 // This is encoded as a sparse map, so perform a binary search.
6475 const ResTable_sparseTypeEntry* sparseIndices =
6476 reinterpret_cast<const ResTable_sparseTypeEntry*>(eindex);
6477 const ResTable_sparseTypeEntry* result = std::lower_bound(
6478 sparseIndices, sparseIndices + dtohl(thisType->entryCount), realEntryIndex,
6479 keyCompare);
6480 if (result == sparseIndices + dtohl(thisType->entryCount)
6481 || dtohs(result->idx) != realEntryIndex) {
6482 // No entry found.
6483 continue;
6484 }
6485
6486 // Extract the offset from the entry. Each offset must be a multiple of 4
6487 // so we store it as the real offset divided by 4.
6488 thisOffset = dtohs(result->offset) * 4u;
6489 } else {
6490 if (static_cast<uint32_t>(realEntryIndex) >= dtohl(thisType->entryCount)) {
6491 // Entry does not exist.
6492 continue;
6493 }
6494
6495 thisOffset = dtohl(eindex[realEntryIndex]);
6496 }
6497
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006498 if (thisOffset == ResTable_type::NO_ENTRY) {
6499 // There is no entry for this index and configuration.
6500 continue;
6501 }
6502
6503 if (bestType != NULL) {
6504 // Check if this one is less specific than the last found. If so,
6505 // we will skip it. We check starting with things we most care
6506 // about to those we least care about.
6507 if (!thisConfig.isBetterThan(bestConfig, config)) {
6508 if (!currentTypeIsOverlay || thisConfig.compare(bestConfig) != 0) {
6509 continue;
6510 }
6511 }
6512 }
6513
6514 bestType = thisType;
6515 bestOffset = thisOffset;
6516 bestConfig = thisConfig;
6517 bestPackage = typeSpec->package;
6518 actualTypeIndex = realTypeIndex;
6519
6520 // If no config was specified, any type will do, so skip
6521 if (config == NULL) {
6522 break;
6523 }
6524 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006525 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07006526
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006527 if (bestType == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006528 return BAD_INDEX;
6529 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07006530
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006531 bestOffset += dtohl(bestType->entriesStart);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006532
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006533 if (bestOffset > (dtohl(bestType->header.size)-sizeof(ResTable_entry))) {
Steve Block8564c8d2012-01-05 23:22:43 +00006534 ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x",
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006535 bestOffset, dtohl(bestType->header.size));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006536 return BAD_TYPE;
6537 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006538 if ((bestOffset & 0x3) != 0) {
6539 ALOGW("ResTable_entry at 0x%x is not on an integer boundary", bestOffset);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006540 return BAD_TYPE;
6541 }
6542
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006543 const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>(
6544 reinterpret_cast<const uint8_t*>(bestType) + bestOffset);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006545 if (dtohs(entry->size) < sizeof(*entry)) {
Steve Block8564c8d2012-01-05 23:22:43 +00006546 ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006547 return BAD_TYPE;
6548 }
6549
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006550 if (outEntry != NULL) {
6551 outEntry->entry = entry;
6552 outEntry->config = bestConfig;
6553 outEntry->type = bestType;
6554 outEntry->specFlags = specFlags;
6555 outEntry->package = bestPackage;
6556 outEntry->typeStr = StringPoolRef(&bestPackage->typeStrings, actualTypeIndex - bestPackage->typeIdOffset);
6557 outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, dtohl(entry->key.index));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006558 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006559 return NO_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006560}
6561
6562status_t ResTable::parsePackage(const ResTable_package* const pkg,
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08006563 const Header* const header, bool appAsLib, bool isSystemAsset)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006564{
6565 const uint8_t* base = (const uint8_t*)pkg;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006566 status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006567 header->dataEnd, "ResTable_package");
6568 if (err != NO_ERROR) {
6569 return (mError=err);
6570 }
6571
Patrik Bannura443dd932014-02-12 13:38:54 +01006572 const uint32_t pkgSize = dtohl(pkg->header.size);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006573
6574 if (dtohl(pkg->typeStrings) >= pkgSize) {
Patrik Bannura443dd932014-02-12 13:38:54 +01006575 ALOGW("ResTable_package type strings at 0x%x are past chunk size 0x%x.",
6576 dtohl(pkg->typeStrings), pkgSize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006577 return (mError=BAD_TYPE);
6578 }
6579 if ((dtohl(pkg->typeStrings)&0x3) != 0) {
Patrik Bannura443dd932014-02-12 13:38:54 +01006580 ALOGW("ResTable_package type strings at 0x%x is not on an integer boundary.",
6581 dtohl(pkg->typeStrings));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006582 return (mError=BAD_TYPE);
6583 }
6584 if (dtohl(pkg->keyStrings) >= pkgSize) {
Patrik Bannura443dd932014-02-12 13:38:54 +01006585 ALOGW("ResTable_package key strings at 0x%x are past chunk size 0x%x.",
6586 dtohl(pkg->keyStrings), pkgSize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006587 return (mError=BAD_TYPE);
6588 }
6589 if ((dtohl(pkg->keyStrings)&0x3) != 0) {
Patrik Bannura443dd932014-02-12 13:38:54 +01006590 ALOGW("ResTable_package key strings at 0x%x is not on an integer boundary.",
6591 dtohl(pkg->keyStrings));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006592 return (mError=BAD_TYPE);
6593 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07006594
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006595 uint32_t id = dtohl(pkg->id);
6596 KeyedVector<uint8_t, IdmapEntries> idmapEntries;
Mark Salyzyn00adb862014-03-19 11:00:06 -07006597
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006598 if (header->resourceIDMap != NULL) {
6599 uint8_t targetPackageId = 0;
6600 status_t err = parseIdmap(header->resourceIDMap, header->resourceIDMapSize, &targetPackageId, &idmapEntries);
6601 if (err != NO_ERROR) {
6602 ALOGW("Overlay is broken");
6603 return (mError=err);
6604 }
6605 id = targetPackageId;
6606 }
6607
Todd Kennedy32512992018-04-25 16:45:59 -07006608 bool isDynamic = false;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006609 if (id >= 256) {
6610 LOG_ALWAYS_FATAL("Package id out of range");
6611 return NO_ERROR;
Adam Lesinski25f48882016-06-14 11:05:23 -07006612 } else if (id == 0 || (id == 0x7f && appAsLib) || isSystemAsset) {
Roozbeh Pournader1c686f22015-12-18 14:22:14 -08006613 // This is a library or a system asset, so assign an ID
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006614 id = mNextPackageId++;
Todd Kennedy32512992018-04-25 16:45:59 -07006615 isDynamic = true;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006616 }
6617
6618 PackageGroup* group = NULL;
6619 Package* package = new Package(this, header, pkg);
6620 if (package == NULL) {
6621 return (mError=NO_MEMORY);
6622 }
6623
6624 err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
6625 header->dataEnd-(base+dtohl(pkg->typeStrings)));
6626 if (err != NO_ERROR) {
6627 delete group;
6628 delete package;
6629 return (mError=err);
6630 }
6631
6632 err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
6633 header->dataEnd-(base+dtohl(pkg->keyStrings)));
6634 if (err != NO_ERROR) {
6635 delete group;
6636 delete package;
6637 return (mError=err);
6638 }
6639
6640 size_t idx = mPackageMap[id];
6641 if (idx == 0) {
6642 idx = mPackageGroups.size() + 1;
Adam Lesinski4bf58102014-11-03 11:21:19 -08006643 char16_t tmpName[sizeof(pkg->name)/sizeof(pkg->name[0])];
6644 strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(pkg->name[0]));
Todd Kennedy32512992018-04-25 16:45:59 -07006645 group = new PackageGroup(this, String16(tmpName), id, appAsLib, isSystemAsset, isDynamic);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006646 if (group == NULL) {
6647 delete package;
Dianne Hackborn78c40512009-07-06 11:07:40 -07006648 return (mError=NO_MEMORY);
6649 }
Adam Lesinskifab50872014-04-16 14:40:42 -07006650
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006651 err = mPackageGroups.add(group);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006652 if (err < NO_ERROR) {
6653 return (mError=err);
6654 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006655
6656 mPackageMap[id] = static_cast<uint8_t>(idx);
6657
6658 // Find all packages that reference this package
6659 size_t N = mPackageGroups.size();
6660 for (size_t i = 0; i < N; i++) {
6661 mPackageGroups[i]->dynamicRefTable.addMapping(
6662 group->name, static_cast<uint8_t>(group->id));
6663 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006664 } else {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006665 group = mPackageGroups.itemAt(idx - 1);
6666 if (group == NULL) {
6667 return (mError=UNKNOWN_ERROR);
6668 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006669 }
6670
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006671 err = group->packages.add(package);
6672 if (err < NO_ERROR) {
6673 return (mError=err);
6674 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07006675
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006676 // Iterate through all chunks.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006677 const ResChunk_header* chunk =
6678 (const ResChunk_header*)(((const uint8_t*)pkg)
6679 + dtohs(pkg->header.headerSize));
6680 const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size);
6681 while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) &&
6682 ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) {
Andreas Gampe2204f0b2014-10-21 23:04:54 -07006683 if (kDebugTableNoisy) {
6684 ALOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n",
6685 dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size),
6686 (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)));
6687 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006688 const size_t csize = dtohl(chunk->size);
6689 const uint16_t ctype = dtohs(chunk->type);
6690 if (ctype == RES_TABLE_TYPE_SPEC_TYPE) {
6691 const ResTable_typeSpec* typeSpec = (const ResTable_typeSpec*)(chunk);
6692 err = validate_chunk(&typeSpec->header, sizeof(*typeSpec),
6693 endPos, "ResTable_typeSpec");
6694 if (err != NO_ERROR) {
6695 return (mError=err);
6696 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07006697
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006698 const size_t typeSpecSize = dtohl(typeSpec->header.size);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006699 const size_t newEntryCount = dtohl(typeSpec->entryCount);
Mark Salyzyn00adb862014-03-19 11:00:06 -07006700
Andreas Gampe2204f0b2014-10-21 23:04:54 -07006701 if (kDebugLoadTableNoisy) {
6702 ALOGI("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n",
6703 (void*)(base-(const uint8_t*)chunk),
6704 dtohs(typeSpec->header.type),
6705 dtohs(typeSpec->header.headerSize),
6706 (void*)typeSpecSize);
6707 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006708 // look for block overrun or int overflow when multiplying by 4
6709 if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t))
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006710 || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*newEntryCount)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006711 > typeSpecSize)) {
Steve Block8564c8d2012-01-05 23:22:43 +00006712 ALOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.",
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006713 (void*)(dtohs(typeSpec->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
6714 (void*)typeSpecSize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006715 return (mError=BAD_TYPE);
6716 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07006717
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006718 if (typeSpec->id == 0) {
Steve Block8564c8d2012-01-05 23:22:43 +00006719 ALOGW("ResTable_type has an id of 0.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006720 return (mError=BAD_TYPE);
6721 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07006722
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006723 if (newEntryCount > 0) {
Adam Lesinskied69ce82017-03-20 10:55:01 -07006724 bool addToType = true;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006725 uint8_t typeIndex = typeSpec->id - 1;
6726 ssize_t idmapIndex = idmapEntries.indexOfKey(typeSpec->id);
6727 if (idmapIndex >= 0) {
6728 typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
Adam Lesinskied69ce82017-03-20 10:55:01 -07006729 } else if (header->resourceIDMap != NULL) {
6730 // This is an overlay, but the types in this overlay are not
6731 // overlaying anything according to the idmap. We can skip these
6732 // as they will otherwise conflict with the other resources in the package
6733 // without a mapping.
6734 addToType = false;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006735 }
6736
Adam Lesinskied69ce82017-03-20 10:55:01 -07006737 if (addToType) {
6738 TypeList& typeList = group->types.editItemAt(typeIndex);
6739 if (!typeList.isEmpty()) {
6740 const Type* existingType = typeList[0];
6741 if (existingType->entryCount != newEntryCount && idmapIndex < 0) {
6742 ALOGW("ResTable_typeSpec entry count inconsistent: "
6743 "given %d, previously %d",
6744 (int) newEntryCount, (int) existingType->entryCount);
6745 // We should normally abort here, but some legacy apps declare
6746 // resources in the 'android' package (old bug in AAPT).
6747 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006748 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006749
Adam Lesinskied69ce82017-03-20 10:55:01 -07006750 Type* t = new Type(header, package, newEntryCount);
6751 t->typeSpec = typeSpec;
6752 t->typeSpecFlags = (const uint32_t*)(
6753 ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
6754 if (idmapIndex >= 0) {
6755 t->idmapEntries = idmapEntries[idmapIndex];
6756 }
6757 typeList.add(t);
6758 group->largestTypeId = max(group->largestTypeId, typeSpec->id);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006759 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006760 } else {
6761 ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec->id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006762 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07006763
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006764 } else if (ctype == RES_TABLE_TYPE_TYPE) {
6765 const ResTable_type* type = (const ResTable_type*)(chunk);
6766 err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4,
6767 endPos, "ResTable_type");
6768 if (err != NO_ERROR) {
6769 return (mError=err);
6770 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07006771
Patrik Bannura443dd932014-02-12 13:38:54 +01006772 const uint32_t typeSize = dtohl(type->header.size);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006773 const size_t newEntryCount = dtohl(type->entryCount);
Mark Salyzyn00adb862014-03-19 11:00:06 -07006774
Andreas Gampe2204f0b2014-10-21 23:04:54 -07006775 if (kDebugLoadTableNoisy) {
6776 printf("Type off %p: type=0x%x, headerSize=0x%x, size=%u\n",
6777 (void*)(base-(const uint8_t*)chunk),
6778 dtohs(type->header.type),
6779 dtohs(type->header.headerSize),
6780 typeSize);
6781 }
6782 if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount) > typeSize) {
Patrik Bannura443dd932014-02-12 13:38:54 +01006783 ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.",
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006784 (void*)(dtohs(type->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
6785 typeSize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006786 return (mError=BAD_TYPE);
6787 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006788
6789 if (newEntryCount != 0
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006790 && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) {
Patrik Bannura443dd932014-02-12 13:38:54 +01006791 ALOGW("ResTable_type entriesStart at 0x%x extends beyond chunk end 0x%x.",
6792 dtohl(type->entriesStart), typeSize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006793 return (mError=BAD_TYPE);
6794 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006795
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006796 if (type->id == 0) {
Steve Block8564c8d2012-01-05 23:22:43 +00006797 ALOGW("ResTable_type has an id of 0.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006798 return (mError=BAD_TYPE);
6799 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07006800
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006801 if (newEntryCount > 0) {
Adam Lesinskied69ce82017-03-20 10:55:01 -07006802 bool addToType = true;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006803 uint8_t typeIndex = type->id - 1;
6804 ssize_t idmapIndex = idmapEntries.indexOfKey(type->id);
6805 if (idmapIndex >= 0) {
6806 typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
Adam Lesinskied69ce82017-03-20 10:55:01 -07006807 } else if (header->resourceIDMap != NULL) {
6808 // This is an overlay, but the types in this overlay are not
6809 // overlaying anything according to the idmap. We can skip these
6810 // as they will otherwise conflict with the other resources in the package
6811 // without a mapping.
6812 addToType = false;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006813 }
6814
Adam Lesinskied69ce82017-03-20 10:55:01 -07006815 if (addToType) {
6816 TypeList& typeList = group->types.editItemAt(typeIndex);
6817 if (typeList.isEmpty()) {
6818 ALOGE("No TypeSpec for type %d", type->id);
6819 return (mError=BAD_TYPE);
6820 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006821
Adam Lesinskied69ce82017-03-20 10:55:01 -07006822 Type* t = typeList.editItemAt(typeList.size() - 1);
6823 if (t->package != package) {
6824 ALOGE("No TypeSpec for type %d", type->id);
6825 return (mError=BAD_TYPE);
6826 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006827
Adam Lesinskied69ce82017-03-20 10:55:01 -07006828 t->configs.add(type);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006829
Adam Lesinskied69ce82017-03-20 10:55:01 -07006830 if (kDebugTableGetEntry) {
6831 ResTable_config thisConfig;
6832 thisConfig.copyFromDtoH(type->config);
6833 ALOGI("Adding config to type %d: %s\n", type->id,
6834 thisConfig.toString().string());
6835 }
Andreas Gampe2204f0b2014-10-21 23:04:54 -07006836 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07006837 } else {
6838 ALOGV("Skipping empty ResTable_type for type %d", type->id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006839 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07006840
Adam Lesinskide898ff2014-01-29 18:20:45 -08006841 } else if (ctype == RES_TABLE_LIBRARY_TYPE) {
Ryan Mitchell18a6ada2018-05-30 12:17:01 -07006842
Adam Lesinskide898ff2014-01-29 18:20:45 -08006843 if (group->dynamicRefTable.entries().size() == 0) {
Ryan Mitchell18a6ada2018-05-30 12:17:01 -07006844 const ResTable_lib_header* lib = (const ResTable_lib_header*) chunk;
6845 status_t err = validate_chunk(&lib->header, sizeof(*lib),
6846 endPos, "ResTable_lib_header");
6847 if (err != NO_ERROR) {
6848 return (mError=err);
6849 }
6850
6851 err = group->dynamicRefTable.load(lib);
Adam Lesinskide898ff2014-01-29 18:20:45 -08006852 if (err != NO_ERROR) {
6853 return (mError=err);
6854 }
6855
6856 // Fill in the reference table with the entries we already know about.
6857 size_t N = mPackageGroups.size();
6858 for (size_t i = 0; i < N; i++) {
6859 group->dynamicRefTable.addMapping(mPackageGroups[i]->name, mPackageGroups[i]->id);
6860 }
6861 } else {
6862 ALOGW("Found multiple library tables, ignoring...");
6863 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006864 } else {
Winson1201ca72019-04-12 16:19:26 -07006865 if (ctype == RES_TABLE_OVERLAYABLE_TYPE) {
6866 package->definesOverlayable = true;
6867 }
6868
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006869 status_t err = validate_chunk(chunk, sizeof(ResChunk_header),
6870 endPos, "ResTable_package:unknown");
6871 if (err != NO_ERROR) {
6872 return (mError=err);
6873 }
6874 }
6875 chunk = (const ResChunk_header*)
6876 (((const uint8_t*)chunk) + csize);
6877 }
6878
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006879 return NO_ERROR;
6880}
6881
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -08006882DynamicRefTable::DynamicRefTable() : DynamicRefTable(0, false) {}
6883
Tao Baia6d7e3f2015-09-01 18:49:54 -07006884DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
Adam Lesinskide898ff2014-01-29 18:20:45 -08006885 : mAssignedPackageId(packageId)
Tao Baia6d7e3f2015-09-01 18:49:54 -07006886 , mAppAsLib(appAsLib)
Adam Lesinskide898ff2014-01-29 18:20:45 -08006887{
6888 memset(mLookupTable, 0, sizeof(mLookupTable));
6889
6890 // Reserved package ids
6891 mLookupTable[APP_PACKAGE_ID] = APP_PACKAGE_ID;
6892 mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID;
6893}
6894
Ryan Mitchella41e66a2018-05-15 15:08:58 -07006895std::unique_ptr<DynamicRefTable> DynamicRefTable::clone() const {
6896 std::unique_ptr<DynamicRefTable> clone = std::unique_ptr<DynamicRefTable>(
6897 new DynamicRefTable(mAssignedPackageId, mAppAsLib));
6898 clone->addMappings(*this);
6899 return clone;
6900}
6901
Adam Lesinskide898ff2014-01-29 18:20:45 -08006902status_t DynamicRefTable::load(const ResTable_lib_header* const header)
6903{
6904 const uint32_t entryCount = dtohl(header->count);
6905 const uint32_t sizeOfEntries = sizeof(ResTable_lib_entry) * entryCount;
6906 const uint32_t expectedSize = dtohl(header->header.size) - dtohl(header->header.headerSize);
6907 if (sizeOfEntries > expectedSize) {
6908 ALOGE("ResTable_lib_header size %u is too small to fit %u entries (x %u).",
6909 expectedSize, entryCount, (uint32_t)sizeof(ResTable_lib_entry));
6910 return UNKNOWN_ERROR;
6911 }
6912
6913 const ResTable_lib_entry* entry = (const ResTable_lib_entry*)(((uint8_t*) header) +
6914 dtohl(header->header.headerSize));
6915 for (uint32_t entryIndex = 0; entryIndex < entryCount; entryIndex++) {
6916 uint32_t packageId = dtohl(entry->packageId);
6917 char16_t tmpName[sizeof(entry->packageName) / sizeof(char16_t)];
6918 strcpy16_dtoh(tmpName, entry->packageName, sizeof(entry->packageName) / sizeof(char16_t));
Andreas Gampe2204f0b2014-10-21 23:04:54 -07006919 if (kDebugLibNoisy) {
6920 ALOGV("Found lib entry %s with id %d\n", String8(tmpName).string(),
6921 dtohl(entry->packageId));
6922 }
Adam Lesinskide898ff2014-01-29 18:20:45 -08006923 if (packageId >= 256) {
6924 ALOGE("Bad package id 0x%08x", packageId);
6925 return UNKNOWN_ERROR;
6926 }
6927 mEntries.replaceValueFor(String16(tmpName), (uint8_t) packageId);
6928 entry = entry + 1;
6929 }
6930 return NO_ERROR;
6931}
6932
Adam Lesinski6022deb2014-08-20 14:59:19 -07006933status_t DynamicRefTable::addMappings(const DynamicRefTable& other) {
6934 if (mAssignedPackageId != other.mAssignedPackageId) {
6935 return UNKNOWN_ERROR;
6936 }
6937
6938 const size_t entryCount = other.mEntries.size();
6939 for (size_t i = 0; i < entryCount; i++) {
6940 ssize_t index = mEntries.indexOfKey(other.mEntries.keyAt(i));
6941 if (index < 0) {
Ryan Mitchella41e66a2018-05-15 15:08:58 -07006942 mEntries.add(String16(other.mEntries.keyAt(i)), other.mEntries[i]);
Adam Lesinski6022deb2014-08-20 14:59:19 -07006943 } else {
6944 if (other.mEntries[i] != mEntries[index]) {
6945 return UNKNOWN_ERROR;
6946 }
6947 }
6948 }
6949
6950 // Merge the lookup table. No entry can conflict
6951 // (value of 0 means not set).
6952 for (size_t i = 0; i < 256; i++) {
6953 if (mLookupTable[i] != other.mLookupTable[i]) {
6954 if (mLookupTable[i] == 0) {
6955 mLookupTable[i] = other.mLookupTable[i];
6956 } else if (other.mLookupTable[i] != 0) {
6957 return UNKNOWN_ERROR;
6958 }
6959 }
6960 }
6961 return NO_ERROR;
6962}
6963
Adam Lesinskide898ff2014-01-29 18:20:45 -08006964status_t DynamicRefTable::addMapping(const String16& packageName, uint8_t packageId)
6965{
6966 ssize_t index = mEntries.indexOfKey(packageName);
6967 if (index < 0) {
6968 return UNKNOWN_ERROR;
6969 }
6970 mLookupTable[mEntries.valueAt(index)] = packageId;
6971 return NO_ERROR;
6972}
6973
Adam Lesinski4ca56972017-04-26 21:49:53 -07006974void DynamicRefTable::addMapping(uint8_t buildPackageId, uint8_t runtimePackageId) {
6975 mLookupTable[buildPackageId] = runtimePackageId;
6976}
6977
Adam Lesinskide898ff2014-01-29 18:20:45 -08006978status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
6979 uint32_t res = *resId;
6980 size_t packageId = Res_GETPACKAGE(res) + 1;
6981
Ryan Mitchellb9b540b2018-08-22 11:22:54 -07006982 if (!Res_VALIDID(res)) {
6983 // Cannot look up a null or invalid id, so no lookup needs to be done.
6984 return NO_ERROR;
6985 }
6986
Tao Baia6d7e3f2015-09-01 18:49:54 -07006987 if (packageId == APP_PACKAGE_ID && !mAppAsLib) {
Adam Lesinskide898ff2014-01-29 18:20:45 -08006988 // No lookup needs to be done, app package IDs are absolute.
6989 return NO_ERROR;
6990 }
6991
Tao Baia6d7e3f2015-09-01 18:49:54 -07006992 if (packageId == 0 || (packageId == APP_PACKAGE_ID && mAppAsLib)) {
Adam Lesinskide898ff2014-01-29 18:20:45 -08006993 // The package ID is 0x00. That means that a shared library is accessing
Tao Baia6d7e3f2015-09-01 18:49:54 -07006994 // its own local resource.
6995 // Or if app resource is loaded as shared library, the resource which has
6996 // app package Id is local resources.
6997 // so we fix up those resources with the calling package ID.
6998 *resId = (0xFFFFFF & (*resId)) | (((uint32_t) mAssignedPackageId) << 24);
Adam Lesinskide898ff2014-01-29 18:20:45 -08006999 return NO_ERROR;
7000 }
7001
7002 // Do a proper lookup.
7003 uint8_t translatedId = mLookupTable[packageId];
7004 if (translatedId == 0) {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -08007005 ALOGW("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.",
Adam Lesinskide898ff2014-01-29 18:20:45 -08007006 (uint8_t)mAssignedPackageId, (uint8_t)packageId);
7007 for (size_t i = 0; i < 256; i++) {
7008 if (mLookupTable[i] != 0) {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -08007009 ALOGW("e[0x%02x] -> 0x%02x", (uint8_t)i, mLookupTable[i]);
Adam Lesinskide898ff2014-01-29 18:20:45 -08007010 }
7011 }
7012 return UNKNOWN_ERROR;
7013 }
7014
7015 *resId = (res & 0x00ffffff) | (((uint32_t) translatedId) << 24);
7016 return NO_ERROR;
7017}
7018
7019status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
Ryan Mitchell5db396d2018-11-05 15:56:15 -08007020 uint8_t resolvedType = Res_value::TYPE_REFERENCE;
7021 switch (value->dataType) {
7022 case Res_value::TYPE_ATTRIBUTE:
7023 resolvedType = Res_value::TYPE_ATTRIBUTE;
Mårten Kongstade0930d32018-10-18 14:50:15 +02007024 FALLTHROUGH_INTENDED;
Ryan Mitchell5db396d2018-11-05 15:56:15 -08007025 case Res_value::TYPE_REFERENCE:
7026 // Only resolve non-dynamic references and attributes if the package is loaded as a
7027 // library or if a shared library is attempting to retrieve its own resource
7028 if (!(mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) {
7029 return NO_ERROR;
7030 }
Adam Lesinski8ac51d12016-05-10 10:01:12 -07007031
Ryan Mitchell5db396d2018-11-05 15:56:15 -08007032 // If the package is loaded as shared library, the resource reference
7033 // also need to be fixed.
7034 break;
7035 case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
7036 resolvedType = Res_value::TYPE_ATTRIBUTE;
Mårten Kongstade0930d32018-10-18 14:50:15 +02007037 FALLTHROUGH_INTENDED;
Ryan Mitchell5db396d2018-11-05 15:56:15 -08007038 case Res_value::TYPE_DYNAMIC_REFERENCE:
7039 break;
7040 default:
7041 return NO_ERROR;
Adam Lesinskide898ff2014-01-29 18:20:45 -08007042 }
7043
7044 status_t err = lookupResourceId(&value->data);
7045 if (err != NO_ERROR) {
7046 return err;
7047 }
7048
Adam Lesinski8ac51d12016-05-10 10:01:12 -07007049 value->dataType = resolvedType;
Adam Lesinskide898ff2014-01-29 18:20:45 -08007050 return NO_ERROR;
7051}
7052
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007053class IdmapMatchingResources;
7054
7055class IdmapTypeMapping {
7056public:
7057 void add(uint32_t targetResId, uint32_t overlayResId) {
7058 uint8_t targetTypeId = Res_GETTYPE(targetResId);
7059 if (mData.find(targetTypeId) == mData.end()) {
7060 mData.emplace(targetTypeId, std::set<std::pair<uint32_t, uint32_t>>());
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007061 }
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007062 auto& entries = mData[targetTypeId];
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007063 entries.insert(std::make_pair(targetResId, overlayResId));
7064 }
7065
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007066 bool empty() const {
7067 return mData.empty();
7068 }
7069
7070private:
7071 // resource type ID in context of target -> set of resource entries mapping target -> overlay
7072 std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>> mData;
7073
7074 friend IdmapMatchingResources;
7075};
7076
7077class IdmapMatchingResources {
7078public:
7079 IdmapMatchingResources(std::unique_ptr<IdmapTypeMapping> tm) : mTypeMapping(std::move(tm)) {
7080 assert(mTypeMapping);
7081 for (auto ti = mTypeMapping->mData.cbegin(); ti != mTypeMapping->mData.cend(); ++ti) {
7082 uint32_t lastSeen = 0xffffffff;
7083 size_t totalEntries = 0;
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007084 for (auto ei = ti->second.cbegin(); ei != ti->second.cend(); ++ei) {
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007085 assert(lastSeen == 0xffffffff || lastSeen < ei->first);
7086 mEntryPadding[ei->first] = (lastSeen == 0xffffffff) ? 0 : ei->first - lastSeen - 1;
7087 lastSeen = ei->first;
7088 totalEntries += 1 + mEntryPadding[ei->first];
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007089 }
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007090 mNumberOfEntriesIncludingPadding[ti->first] = totalEntries;
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007091 }
7092 }
7093
Ryan Mitchell75e20dd2018-11-06 16:39:36 -08007094 const std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>>& getTypeMapping() const {
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007095 return mTypeMapping->mData;
7096 }
7097
7098 size_t getNumberOfEntriesIncludingPadding(uint8_t type) const {
7099 return mNumberOfEntriesIncludingPadding.at(type);
7100 }
7101
7102 size_t getPadding(uint32_t resid) const {
7103 return mEntryPadding.at(resid);
7104 }
7105
7106private:
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007107 // resource type ID in context of target -> set of resource entries mapping target -> overlay
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007108 const std::unique_ptr<IdmapTypeMapping> mTypeMapping;
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007109
7110 // resource ID in context of target -> trailing padding for that resource (call FixPadding
7111 // before use)
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007112 std::map<uint32_t, size_t> mEntryPadding;
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007113
7114 // resource type ID in context of target -> total number of entries, including padding entries,
7115 // for that type (call FixPadding before use)
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007116 std::map<uint8_t, size_t> mNumberOfEntriesIncludingPadding;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007117};
7118
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007119status_t ResTable::createIdmap(const ResTable& targetResTable,
Mårten Kongstad48d22322014-01-31 14:43:27 +01007120 uint32_t targetCrc, uint32_t overlayCrc,
7121 const char* targetPath, const char* overlayPath,
7122 void** outData, size_t* outSize) const
Mårten Kongstad57f4b772011-03-17 14:13:41 +01007123{
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007124 if (targetPath == NULL || overlayPath == NULL || outData == NULL || outSize == NULL) {
7125 ALOGE("idmap: unexpected NULL parameter");
7126 return UNKNOWN_ERROR;
7127 }
7128 if (strlen(targetPath) > 255) {
7129 ALOGE("idmap: target path exceeds idmap file format limit of 255 chars");
7130 return UNKNOWN_ERROR;
7131 }
7132 if (strlen(overlayPath) > 255) {
7133 ALOGE("idmap: overlay path exceeds idmap file format limit of 255 chars");
7134 return UNKNOWN_ERROR;
7135 }
7136 if (mPackageGroups.size() == 0 || mPackageGroups[0]->packages.size() == 0) {
7137 ALOGE("idmap: invalid overlay package");
7138 return UNKNOWN_ERROR;
7139 }
7140 if (targetResTable.mPackageGroups.size() == 0 ||
7141 targetResTable.mPackageGroups[0]->packages.size() == 0) {
7142 ALOGE("idmap: invalid target package");
Mårten Kongstad57f4b772011-03-17 14:13:41 +01007143 return UNKNOWN_ERROR;
7144 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007145
Winson1201ca72019-04-12 16:19:26 -07007146 // Idmap is not aware of overlayable, exit since policy checks can't be done
7147 if (targetResTable.mPackageGroups[0]->packages[0]->definesOverlayable) {
7148 return UNKNOWN_ERROR;
7149 }
7150
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007151 const ResTable_package* targetPackageStruct =
7152 targetResTable.mPackageGroups[0]->packages[0]->package;
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007153 const size_t tmpNameSize = arraysize(targetPackageStruct->name);
7154 char16_t tmpName[tmpNameSize];
7155 strcpy16_dtoh(tmpName, targetPackageStruct->name, tmpNameSize);
7156 const String16 targetPackageName(tmpName);
Mårten Kongstad57f4b772011-03-17 14:13:41 +01007157
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007158 const PackageGroup* packageGroup = mPackageGroups[0];
7159
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007160 // find the resources that exist in both packages
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007161 auto typeMapping = std::make_unique<IdmapTypeMapping>();
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007162 for (size_t typeIndex = 0; typeIndex < packageGroup->types.size(); ++typeIndex) {
7163 const TypeList& typeList = packageGroup->types[typeIndex];
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007164 if (typeList.isEmpty()) {
7165 continue;
Mårten Kongstad57f4b772011-03-17 14:13:41 +01007166 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007167 const Type* typeConfigs = typeList[0];
7168
Mårten Kongstad57f4b772011-03-17 14:13:41 +01007169 for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007170 uint32_t overlay_resid = Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex);
7171 resource_name current_res;
7172 if (!getResourceName(overlay_resid, false, &current_res)) {
Mårten Kongstadfcaba142011-05-19 16:02:35 +02007173 continue;
Mårten Kongstad57f4b772011-03-17 14:13:41 +01007174 }
7175
Adam Lesinskia9743822017-12-18 17:20:41 -08007176 uint32_t typeSpecFlags = 0u;
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007177 const uint32_t target_resid = targetResTable.identifierForName(
7178 current_res.name,
7179 current_res.nameLen,
7180 current_res.type,
7181 current_res.typeLen,
7182 targetPackageName.string(),
7183 targetPackageName.size(),
7184 &typeSpecFlags);
7185
7186 if (target_resid == 0) {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007187 continue;
Mårten Kongstad57f4b772011-03-17 14:13:41 +01007188 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007189
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007190 typeMapping->add(target_resid, overlay_resid);
Mårten Kongstad57f4b772011-03-17 14:13:41 +01007191 }
7192 }
7193
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007194 if (typeMapping->empty()) {
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007195 ALOGE("idmap: no matching resources");
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007196 return UNKNOWN_ERROR;
7197 }
7198
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007199 const IdmapMatchingResources matchingResources(std::move(typeMapping));
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007200
7201 // write idmap
7202 *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES; // magic, version, target and overlay crc
7203 *outSize += 2 * sizeof(uint16_t); // target package id, type count
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007204 auto fixedTypeMapping = matchingResources.getTypeMapping();
7205 const auto typesEnd = fixedTypeMapping.cend();
7206 for (auto ti = fixedTypeMapping.cbegin(); ti != typesEnd; ++ti) {
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007207 *outSize += 4 * sizeof(uint16_t); // target type, overlay type, entry count, entry offset
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007208 *outSize += matchingResources.getNumberOfEntriesIncludingPadding(ti->first) *
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007209 sizeof(uint32_t); // entries
7210 }
Mårten Kongstad57f4b772011-03-17 14:13:41 +01007211 if ((*outData = malloc(*outSize)) == NULL) {
7212 return NO_MEMORY;
7213 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007214
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007215 // write idmap header
7216 uint32_t* data = reinterpret_cast<uint32_t*>(*outData);
7217 *data++ = htodl(IDMAP_MAGIC); // write: magic
7218 *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION); // write: version
7219 *data++ = htodl(targetCrc); // write: target crc
7220 *data++ = htodl(overlayCrc); // write: overlay crc
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007221
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007222 char* charData = reinterpret_cast<char*>(data);
7223 size_t pathLen = strlen(targetPath);
7224 for (size_t i = 0; i < 256; ++i) {
7225 *charData++ = i < pathLen ? targetPath[i] : '\0'; // write: target path
7226 }
7227 pathLen = strlen(overlayPath);
7228 for (size_t i = 0; i < 256; ++i) {
7229 *charData++ = i < pathLen ? overlayPath[i] : '\0'; // write: overlay path
7230 }
7231 data += (2 * 256) / sizeof(uint32_t);
7232
7233 // write idmap data header
7234 uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
7235 *typeData++ = htods(targetPackageStruct->id); // write: target package id
7236 *typeData++ =
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007237 htods(static_cast<uint16_t>(fixedTypeMapping.size())); // write: type count
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007238
7239 // write idmap data
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007240 for (auto ti = fixedTypeMapping.cbegin(); ti != typesEnd; ++ti) {
7241 const size_t entryCount = matchingResources.getNumberOfEntriesIncludingPadding(ti->first);
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007242 auto ei = ti->second.cbegin();
7243 *typeData++ = htods(Res_GETTYPE(ei->first) + 1); // write: target type id
7244 *typeData++ = htods(Res_GETTYPE(ei->second) + 1); // write: overlay type id
7245 *typeData++ = htods(entryCount); // write: entry count
7246 *typeData++ = htods(Res_GETENTRY(ei->first)); // write: (target) entry offset
7247 uint32_t *entryData = reinterpret_cast<uint32_t*>(typeData);
7248 for (; ei != ti->second.cend(); ++ei) {
Mårten Kongstad59d5a5a2018-11-27 13:46:58 +01007249 const size_t padding = matchingResources.getPadding(ei->first);
Mårten Kongstad67d5c932018-05-25 15:58:17 +02007250 for (size_t i = 0; i < padding; ++i) {
7251 *entryData++ = htodl(0xffffffff); // write: padding
7252 }
7253 *entryData++ = htodl(Res_GETENTRY(ei->second)); // write: (overlay) entry
Mårten Kongstad57f4b772011-03-17 14:13:41 +01007254 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007255 typeData += entryCount * 2;
Mårten Kongstad57f4b772011-03-17 14:13:41 +01007256 }
7257
7258 return NO_ERROR;
7259}
7260
7261bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007262 uint32_t* pVersion,
Mårten Kongstad48d22322014-01-31 14:43:27 +01007263 uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
7264 String8* pTargetPath, String8* pOverlayPath)
Mårten Kongstad57f4b772011-03-17 14:13:41 +01007265{
7266 const uint32_t* map = (const uint32_t*)idmap;
7267 if (!assertIdmapHeader(map, sizeBytes)) {
7268 return false;
7269 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007270 if (pVersion) {
7271 *pVersion = dtohl(map[1]);
7272 }
Mårten Kongstad48d22322014-01-31 14:43:27 +01007273 if (pTargetCrc) {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007274 *pTargetCrc = dtohl(map[2]);
Mårten Kongstad48d22322014-01-31 14:43:27 +01007275 }
7276 if (pOverlayCrc) {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007277 *pOverlayCrc = dtohl(map[3]);
Mårten Kongstad48d22322014-01-31 14:43:27 +01007278 }
7279 if (pTargetPath) {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007280 pTargetPath->setTo(reinterpret_cast<const char*>(map + 4));
Mårten Kongstad48d22322014-01-31 14:43:27 +01007281 }
7282 if (pOverlayPath) {
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007283 pOverlayPath->setTo(reinterpret_cast<const char*>(map + 4 + 256 / sizeof(uint32_t)));
Mårten Kongstad48d22322014-01-31 14:43:27 +01007284 }
Mårten Kongstad57f4b772011-03-17 14:13:41 +01007285 return true;
7286}
7287
7288
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007289#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string())
7290
7291#define CHAR16_ARRAY_EQ(constant, var, len) \
Chih-Hung Hsiehe819d012016-05-19 15:19:22 -07007292 (((len) == (sizeof(constant)/sizeof((constant)[0]))) && (0 == memcmp((var), (constant), (len))))
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007293
Jeff Brown9d3b1a42013-07-01 19:07:15 -07007294static void print_complex(uint32_t complex, bool isFraction)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007295{
Dianne Hackborne17086b2009-06-19 15:13:28 -07007296 const float MANTISSA_MULT =
7297 1.0f / (1<<Res_value::COMPLEX_MANTISSA_SHIFT);
7298 const float RADIX_MULTS[] = {
7299 1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT,
7300 1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT
7301 };
7302
7303 float value = (complex&(Res_value::COMPLEX_MANTISSA_MASK
7304 <<Res_value::COMPLEX_MANTISSA_SHIFT))
7305 * RADIX_MULTS[(complex>>Res_value::COMPLEX_RADIX_SHIFT)
7306 & Res_value::COMPLEX_RADIX_MASK];
7307 printf("%f", value);
Mark Salyzyn00adb862014-03-19 11:00:06 -07007308
Dianne Hackbornde7faf62009-06-30 13:27:30 -07007309 if (!isFraction) {
Dianne Hackborne17086b2009-06-19 15:13:28 -07007310 switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
7311 case Res_value::COMPLEX_UNIT_PX: printf("px"); break;
7312 case Res_value::COMPLEX_UNIT_DIP: printf("dp"); break;
7313 case Res_value::COMPLEX_UNIT_SP: printf("sp"); break;
7314 case Res_value::COMPLEX_UNIT_PT: printf("pt"); break;
7315 case Res_value::COMPLEX_UNIT_IN: printf("in"); break;
7316 case Res_value::COMPLEX_UNIT_MM: printf("mm"); break;
7317 default: printf(" (unknown unit)"); break;
7318 }
7319 } else {
7320 switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
7321 case Res_value::COMPLEX_UNIT_FRACTION: printf("%%"); break;
7322 case Res_value::COMPLEX_UNIT_FRACTION_PARENT: printf("%%p"); break;
7323 default: printf(" (unknown unit)"); break;
7324 }
7325 }
7326}
7327
Shachar Shemesh9872bf42010-12-20 17:38:33 +02007328// Normalize a string for output
7329String8 ResTable::normalizeForOutput( const char *input )
7330{
7331 String8 ret;
7332 char buff[2];
7333 buff[1] = '\0';
7334
7335 while (*input != '\0') {
7336 switch (*input) {
7337 // All interesting characters are in the ASCII zone, so we are making our own lives
7338 // easier by scanning the string one byte at a time.
7339 case '\\':
7340 ret += "\\\\";
7341 break;
7342 case '\n':
7343 ret += "\\n";
7344 break;
7345 case '"':
7346 ret += "\\\"";
7347 break;
7348 default:
7349 buff[0] = *input;
7350 ret += buff;
7351 break;
7352 }
7353
7354 input++;
7355 }
7356
7357 return ret;
7358}
7359
Dianne Hackbornde7faf62009-06-30 13:27:30 -07007360void ResTable::print_value(const Package* pkg, const Res_value& value) const
7361{
7362 if (value.dataType == Res_value::TYPE_NULL) {
Alan Viverettef2969402014-10-29 17:09:36 -07007363 if (value.data == Res_value::DATA_NULL_UNDEFINED) {
7364 printf("(null)\n");
7365 } else if (value.data == Res_value::DATA_NULL_EMPTY) {
7366 printf("(null empty)\n");
7367 } else {
7368 // This should never happen.
7369 printf("(null) 0x%08x\n", value.data);
7370 }
Dianne Hackbornde7faf62009-06-30 13:27:30 -07007371 } else if (value.dataType == Res_value::TYPE_REFERENCE) {
7372 printf("(reference) 0x%08x\n", value.data);
Adam Lesinskide898ff2014-01-29 18:20:45 -08007373 } else if (value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE) {
7374 printf("(dynamic reference) 0x%08x\n", value.data);
Dianne Hackbornde7faf62009-06-30 13:27:30 -07007375 } else if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
7376 printf("(attribute) 0x%08x\n", value.data);
Adam Lesinski8ac51d12016-05-10 10:01:12 -07007377 } else if (value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
7378 printf("(dynamic attribute) 0x%08x\n", value.data);
Dianne Hackbornde7faf62009-06-30 13:27:30 -07007379 } else if (value.dataType == Res_value::TYPE_STRING) {
7380 size_t len;
Kenny Root780d2a12010-02-22 22:36:26 -08007381 const char* str8 = pkg->header->values.string8At(
Dianne Hackbornde7faf62009-06-30 13:27:30 -07007382 value.data, &len);
Kenny Root780d2a12010-02-22 22:36:26 -08007383 if (str8 != NULL) {
Shachar Shemesh9872bf42010-12-20 17:38:33 +02007384 printf("(string8) \"%s\"\n", normalizeForOutput(str8).string());
Dianne Hackbornde7faf62009-06-30 13:27:30 -07007385 } else {
Kenny Root780d2a12010-02-22 22:36:26 -08007386 const char16_t* str16 = pkg->header->values.stringAt(
7387 value.data, &len);
7388 if (str16 != NULL) {
7389 printf("(string16) \"%s\"\n",
Shachar Shemesh9872bf42010-12-20 17:38:33 +02007390 normalizeForOutput(String8(str16, len).string()).string());
Kenny Root780d2a12010-02-22 22:36:26 -08007391 } else {
7392 printf("(string) null\n");
7393 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07007394 }
Dianne Hackbornde7faf62009-06-30 13:27:30 -07007395 } else if (value.dataType == Res_value::TYPE_FLOAT) {
7396 printf("(float) %g\n", *(const float*)&value.data);
7397 } else if (value.dataType == Res_value::TYPE_DIMENSION) {
7398 printf("(dimension) ");
7399 print_complex(value.data, false);
7400 printf("\n");
7401 } else if (value.dataType == Res_value::TYPE_FRACTION) {
7402 printf("(fraction) ");
7403 print_complex(value.data, true);
7404 printf("\n");
7405 } else if (value.dataType >= Res_value::TYPE_FIRST_COLOR_INT
7406 || value.dataType <= Res_value::TYPE_LAST_COLOR_INT) {
7407 printf("(color) #%08x\n", value.data);
7408 } else if (value.dataType == Res_value::TYPE_INT_BOOLEAN) {
7409 printf("(boolean) %s\n", value.data ? "true" : "false");
7410 } else if (value.dataType >= Res_value::TYPE_FIRST_INT
7411 || value.dataType <= Res_value::TYPE_LAST_INT) {
7412 printf("(int) 0x%08x or %d\n", value.data, value.data);
7413 } else {
7414 printf("(unknown type) t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)\n",
7415 (int)value.dataType, (int)value.data,
7416 (int)value.size, (int)value.res0);
7417 }
7418}
7419
Dianne Hackborne17086b2009-06-19 15:13:28 -07007420void ResTable::print(bool inclValues) const
7421{
7422 if (mError != 0) {
7423 printf("mError=0x%x (%s)\n", mError, strerror(mError));
7424 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007425 size_t pgCount = mPackageGroups.size();
7426 printf("Package Groups (%d)\n", (int)pgCount);
7427 for (size_t pgIndex=0; pgIndex<pgCount; pgIndex++) {
7428 const PackageGroup* pg = mPackageGroups[pgIndex];
Adam Lesinski6022deb2014-08-20 14:59:19 -07007429 printf("Package Group %d id=0x%02x packageCount=%d name=%s\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007430 (int)pgIndex, pg->id, (int)pg->packages.size(),
7431 String8(pg->name).string());
Mark Salyzyn00adb862014-03-19 11:00:06 -07007432
Adam Lesinski6022deb2014-08-20 14:59:19 -07007433 const KeyedVector<String16, uint8_t>& refEntries = pg->dynamicRefTable.entries();
7434 const size_t refEntryCount = refEntries.size();
7435 if (refEntryCount > 0) {
7436 printf(" DynamicRefTable entryCount=%d:\n", (int) refEntryCount);
7437 for (size_t refIndex = 0; refIndex < refEntryCount; refIndex++) {
7438 printf(" 0x%02x -> %s\n",
7439 refEntries.valueAt(refIndex),
7440 String8(refEntries.keyAt(refIndex)).string());
7441 }
7442 printf("\n");
7443 }
7444
Ryan Mitchell30487e02018-05-04 14:16:20 -07007445 // Determine the number of resource splits for the resource types in this package.
7446 // It needs to be done outside of the loop below so all of the information for a
7447 // is displayed in a single block. Otherwise, a resource split's resource types
7448 // would be interleaved with other splits.
7449 size_t splitCount = 0;
7450 for (size_t typeIndex = 0; typeIndex < pg->types.size(); typeIndex++) {
7451 splitCount = max(splitCount, pg->types[typeIndex].size());
Adam Lesinski18560882014-08-15 17:18:21 +00007452 }
7453
Ryan Mitchell30487e02018-05-04 14:16:20 -07007454 int packageId = pg->id;
7455 for (size_t splitIndex = 0; splitIndex < splitCount; splitIndex++) {
7456 size_t pkgCount = pg->packages.size();
7457 for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
7458 const Package* pkg = pg->packages[pkgIndex];
7459 // Use a package's real ID, since the ID may have been assigned
7460 // if this package is a shared library.
7461 packageId = pkg->package->id;
7462 char16_t tmpName[sizeof(pkg->package->name)/sizeof(pkg->package->name[0])];
7463 strcpy16_dtoh(tmpName, pkg->package->name,
7464 sizeof(pkg->package->name)/sizeof(pkg->package->name[0]));
7465 printf(" Package %d id=0x%02x name=%s\n", (int)pkgIndex,
7466 pkg->package->id, String8(tmpName).string());
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007467 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007468
Ryan Mitchell30487e02018-05-04 14:16:20 -07007469 for (size_t typeIndex = 0; typeIndex < pg->types.size(); typeIndex++) {
7470 const TypeList& typeList = pg->types[typeIndex];
7471 if (splitIndex >= typeList.size() || typeList.isEmpty()) {
7472 // Only dump if the split exists and contains entries for this type
7473 continue;
7474 }
7475 const Type* typeConfigs = typeList[splitIndex];
7476 const size_t NTC = typeConfigs->configs.size();
7477 printf(" type %d configCount=%d entryCount=%d\n",
7478 (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount);
7479 if (typeConfigs->typeSpecFlags != NULL) {
7480 for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) {
7481 uint32_t resID = (0xff000000 & ((packageId)<<24))
7482 | (0x00ff0000 & ((typeIndex+1)<<16))
7483 | (0x0000ffff & (entryIndex));
7484 // Since we are creating resID without actually
7485 // iterating over them, we have no idea which is a
7486 // dynamic reference. We must check.
7487 if (packageId == 0) {
7488 pg->dynamicRefTable.lookupResourceId(&resID);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007489 }
Ryan Mitchell30487e02018-05-04 14:16:20 -07007490
7491 resource_name resName;
7492 if (this->getResourceName(resID, true, &resName)) {
7493 String8 type8;
7494 String8 name8;
7495 if (resName.type8 != NULL) {
7496 type8 = String8(resName.type8, resName.typeLen);
7497 } else {
7498 type8 = String8(resName.type, resName.typeLen);
7499 }
7500 if (resName.name8 != NULL) {
7501 name8 = String8(resName.name8, resName.nameLen);
7502 } else {
7503 name8 = String8(resName.name, resName.nameLen);
7504 }
7505 printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
7506 resID,
7507 CHAR16_TO_CSTR(resName.package, resName.packageLen),
7508 type8.string(), name8.string(),
7509 dtohl(typeConfigs->typeSpecFlags[entryIndex]));
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007510 } else {
Ryan Mitchell30487e02018-05-04 14:16:20 -07007511 printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007512 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007513 }
7514 }
Ryan Mitchell30487e02018-05-04 14:16:20 -07007515 for (size_t configIndex=0; configIndex<NTC; configIndex++) {
7516 const ResTable_type* type = typeConfigs->configs[configIndex];
7517 if ((((uint64_t)type)&0x3) != 0) {
7518 printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type);
7519 continue;
Adam Lesinskic8f71aa2017-02-08 07:03:50 -08007520 }
Adam Lesinskic8f71aa2017-02-08 07:03:50 -08007521
Ryan Mitchell30487e02018-05-04 14:16:20 -07007522 // Always copy the config, as fields get added and we need to
7523 // set the defaults.
7524 ResTable_config thisConfig;
7525 thisConfig.copyFromDtoH(type->config);
Adam Lesinskic8f71aa2017-02-08 07:03:50 -08007526
Ryan Mitchell30487e02018-05-04 14:16:20 -07007527 String8 configStr = thisConfig.toString();
7528 printf(" config %s", configStr.size() > 0
7529 ? configStr.string() : "(default)");
7530 if (type->flags != 0u) {
7531 printf(" flags=0x%02x", type->flags);
7532 if (type->flags & ResTable_type::FLAG_SPARSE) {
7533 printf(" [sparse]");
7534 }
7535 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007536
Ryan Mitchell30487e02018-05-04 14:16:20 -07007537 printf(":\n");
7538
7539 size_t entryCount = dtohl(type->entryCount);
7540 uint32_t entriesStart = dtohl(type->entriesStart);
7541 if ((entriesStart&0x3) != 0) {
7542 printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n",
7543 entriesStart);
7544 continue;
7545 }
7546 uint32_t typeSize = dtohl(type->header.size);
7547 if ((typeSize&0x3) != 0) {
7548 printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
7549 continue;
7550 }
7551
7552 const uint32_t* const eindex = (const uint32_t*)
7553 (((const uint8_t*)type) + dtohs(type->header.headerSize));
7554 for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
7555 size_t entryId;
7556 uint32_t thisOffset;
7557 if (type->flags & ResTable_type::FLAG_SPARSE) {
7558 const ResTable_sparseTypeEntry* entry =
7559 reinterpret_cast<const ResTable_sparseTypeEntry*>(
7560 eindex + entryIndex);
7561 entryId = dtohs(entry->idx);
7562 // Offsets are encoded as divided by 4.
7563 thisOffset = static_cast<uint32_t>(dtohs(entry->offset)) * 4u;
7564 } else {
7565 entryId = entryIndex;
7566 thisOffset = dtohl(eindex[entryIndex]);
7567 if (thisOffset == ResTable_type::NO_ENTRY) {
7568 continue;
7569 }
7570 }
7571
7572 uint32_t resID = (0xff000000 & ((packageId)<<24))
7573 | (0x00ff0000 & ((typeIndex+1)<<16))
7574 | (0x0000ffff & (entryId));
7575 if (packageId == 0) {
7576 pg->dynamicRefTable.lookupResourceId(&resID);
7577 }
7578 resource_name resName;
7579 if (this->getResourceName(resID, true, &resName)) {
7580 String8 type8;
7581 String8 name8;
7582 if (resName.type8 != NULL) {
7583 type8 = String8(resName.type8, resName.typeLen);
7584 } else {
7585 type8 = String8(resName.type, resName.typeLen);
7586 }
7587 if (resName.name8 != NULL) {
7588 name8 = String8(resName.name8, resName.nameLen);
7589 } else {
7590 name8 = String8(resName.name, resName.nameLen);
7591 }
7592 printf(" resource 0x%08x %s:%s/%s: ", resID,
7593 CHAR16_TO_CSTR(resName.package, resName.packageLen),
7594 type8.string(), name8.string());
7595 } else {
7596 printf(" INVALID RESOURCE 0x%08x: ", resID);
7597 }
7598 if ((thisOffset&0x3) != 0) {
7599 printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset);
Adam Lesinskic8f71aa2017-02-08 07:03:50 -08007600 continue;
7601 }
Ryan Mitchell30487e02018-05-04 14:16:20 -07007602 if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
7603 printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n",
7604 entriesStart, thisOffset, typeSize);
7605 continue;
Kenny Root33791952010-06-08 10:16:48 -07007606 }
Ryan Mitchell30487e02018-05-04 14:16:20 -07007607
7608 const ResTable_entry* ent = (const ResTable_entry*)
7609 (((const uint8_t*)type) + entriesStart + thisOffset);
7610 if (((entriesStart + thisOffset)&0x3) != 0) {
7611 printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n",
7612 (entriesStart + thisOffset));
7613 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007614 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07007615
Ryan Mitchell30487e02018-05-04 14:16:20 -07007616 uintptr_t esize = dtohs(ent->size);
7617 if ((esize&0x3) != 0) {
7618 printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
7619 continue;
7620 }
7621 if ((thisOffset+esize) > typeSize) {
7622 printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n",
7623 entriesStart, thisOffset, (void *)esize, typeSize);
7624 continue;
7625 }
Mark Salyzyn00adb862014-03-19 11:00:06 -07007626
Ryan Mitchell30487e02018-05-04 14:16:20 -07007627 const Res_value* valuePtr = NULL;
7628 const ResTable_map_entry* bagPtr = NULL;
7629 Res_value value;
7630 if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
7631 printf("<bag>");
7632 bagPtr = (const ResTable_map_entry*)ent;
7633 } else {
7634 valuePtr = (const Res_value*)
7635 (((const uint8_t*)ent) + esize);
7636 value.copyFrom_dtoh(*valuePtr);
7637 printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
7638 (int)value.dataType, (int)value.data,
7639 (int)value.size, (int)value.res0);
7640 }
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007641
Ryan Mitchell30487e02018-05-04 14:16:20 -07007642 if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
7643 printf(" (PUBLIC)");
7644 }
7645 printf("\n");
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007646
Ryan Mitchell30487e02018-05-04 14:16:20 -07007647 if (inclValues) {
7648 if (valuePtr != NULL) {
7649 printf(" ");
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -07007650 print_value(typeConfigs->package, value);
Ryan Mitchell30487e02018-05-04 14:16:20 -07007651 } else if (bagPtr != NULL) {
7652 const int N = dtohl(bagPtr->count);
7653 const uint8_t* baseMapPtr = (const uint8_t*)ent;
7654 size_t mapOffset = esize;
7655 const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
7656 const uint32_t parent = dtohl(bagPtr->parent.ident);
7657 uint32_t resolvedParent = parent;
7658 if (Res_GETPACKAGE(resolvedParent) + 1 == 0) {
7659 status_t err =
7660 pg->dynamicRefTable.lookupResourceId(&resolvedParent);
7661 if (err != NO_ERROR) {
7662 resolvedParent = 0;
7663 }
7664 }
7665 printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n",
7666 parent, resolvedParent, N);
7667 for (int i=0;
7668 i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
7669 printf(" #%i (Key=0x%08x): ",
7670 i, dtohl(mapPtr->name.ident));
7671 value.copyFrom_dtoh(mapPtr->value);
7672 print_value(typeConfigs->package, value);
7673 const size_t size = dtohs(mapPtr->value.size);
7674 mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
7675 mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
7676 }
Dianne Hackborne17086b2009-06-19 15:13:28 -07007677 }
7678 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007679 }
7680 }
7681 }
7682 }
7683 }
7684}
7685
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007686} // namespace android