1 package org.robolectric.res.android; 2 3 import static com.google.common.primitives.UnsignedBytes.max; 4 import static org.robolectric.res.android.Errors.BAD_INDEX; 5 import static org.robolectric.res.android.Errors.BAD_TYPE; 6 import static org.robolectric.res.android.Errors.BAD_VALUE; 7 import static org.robolectric.res.android.Errors.NO_ERROR; 8 import static org.robolectric.res.android.Errors.NO_MEMORY; 9 import static org.robolectric.res.android.Errors.UNKNOWN_ERROR; 10 import static org.robolectric.res.android.ResourceTypes.RES_STRING_POOL_TYPE; 11 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_LIBRARY_TYPE; 12 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_PACKAGE_TYPE; 13 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_TYPE; 14 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_TYPE_SPEC_TYPE; 15 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_TYPE_TYPE; 16 import static org.robolectric.res.android.ResourceTypes.validate_chunk; 17 import static org.robolectric.res.android.Util.ALOGD; 18 import static org.robolectric.res.android.Util.ALOGE; 19 import static org.robolectric.res.android.Util.ALOGI; 20 import static org.robolectric.res.android.Util.ALOGV; 21 import static org.robolectric.res.android.Util.ALOGW; 22 import static org.robolectric.res.android.Util.LOG_FATAL_IF; 23 import static org.robolectric.res.android.Util.dtohl; 24 import static org.robolectric.res.android.Util.dtohs; 25 import static org.robolectric.res.android.Util.htodl; 26 import static org.robolectric.res.android.Util.htods; 27 import static org.robolectric.res.android.Util.isTruthy; 28 29 import java.nio.ByteBuffer; 30 import java.nio.ByteOrder; 31 import java.util.ArrayList; 32 import java.util.Collections; 33 import java.util.HashMap; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.Objects; 37 import java.util.concurrent.Semaphore; 38 import org.robolectric.res.android.ResourceTypes.ResChunk_header; 39 import org.robolectric.res.android.ResourceTypes.ResTable_entry; 40 import org.robolectric.res.android.ResourceTypes.ResTable_header; 41 import org.robolectric.res.android.ResourceTypes.ResTable_map; 42 import org.robolectric.res.android.ResourceTypes.ResTable_map_entry; 43 import org.robolectric.res.android.ResourceTypes.ResTable_package; 44 import org.robolectric.res.android.ResourceTypes.ResTable_sparseTypeEntry; 45 import org.robolectric.res.android.ResourceTypes.ResTable_type; 46 import org.robolectric.res.android.ResourceTypes.ResTable_typeSpec; 47 import org.robolectric.res.android.ResourceTypes.Res_value; 48 49 // transliterated from 50 // https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/ResourceTypes.cpp 51 // and 52 // https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/include/androidfw/ResourceTypes.h 53 @SuppressWarnings("NewApi") 54 public class ResTable { 55 56 @SuppressWarnings("unused") 57 private static final int IDMAP_MAGIC = 0x504D4449; 58 59 @SuppressWarnings("unused") 60 private static final int IDMAP_CURRENT_VERSION = 0x00000001; 61 62 static final int APP_PACKAGE_ID = 0x7f; 63 static final int SYS_PACKAGE_ID = 0x01; 64 65 static final boolean kDebugStringPoolNoisy = false; 66 static final boolean kDebugXMLNoisy = false; 67 static final boolean kDebugTableNoisy = false; 68 static final boolean kDebugTableGetEntry = false; 69 static final boolean kDebugTableSuperNoisy = false; 70 static final boolean kDebugLoadTableNoisy = false; 71 static final boolean kDebugLoadTableSuperNoisy = false; 72 static final boolean kDebugTableTheme = false; 73 static final boolean kDebugResXMLTree = false; 74 static final boolean kDebugLibNoisy = false; 75 76 private static final Object NULL = null; 77 public static final bag_set SENTINEL_BAG_SET = new bag_set(1); 78 79 final Semaphore mLock = new Semaphore(1); 80 81 // Mutex that controls access to the list of pre-filtered configurations 82 // to check when looking up entries. 83 // When iterating over a bag, the mLock mutex is locked. While mLock is locked, 84 // we do resource lookups. 85 // Mutex is not reentrant, so we must use a different lock than mLock. 86 final Object mFilteredConfigLock = new Object(); 87 88 // type defined in Errors 89 int mError; 90 91 ResTable_config mParams; 92 93 // Array of all resource tables. 94 final List<Header> mHeaders = new ArrayList<>(); 95 96 // Array of packages in all resource tables. 97 final Map<Integer, PackageGroup> mPackageGroups = new HashMap<>(); 98 99 // Mapping from resource package IDs to indices into the internal 100 // package array. 101 final byte[] mPackageMap = new byte[256]; 102 103 byte mNextPackageId; 104 Res_CHECKID(int resid)105 static boolean Res_CHECKID(int resid) { return ((resid&0xFFFF0000) != 0);} Res_GETPACKAGE(int id)106 static int Res_GETPACKAGE(int id) { 107 return ((id>>24)-1); 108 } Res_GETTYPE(int id)109 public static int Res_GETTYPE(int id) { 110 return (((id>>16)&0xFF)-1); 111 } Res_GETENTRY(int id)112 static int Res_GETENTRY(int id) { 113 return (id&0xFFFF); 114 } Res_MAKEARRAY(int entry)115 static int Res_MAKEARRAY(int entry) { return (0x02000000 | (entry&0xFFFF)); } Res_INTERNALID(int resid)116 static boolean Res_INTERNALID(int resid) { return ((resid&0xFFFF0000) != 0 && (resid&0xFF0000) == 0); } 117 getResourcePackageIndex(int resID)118 int getResourcePackageIndex(int resID) 119 { 120 return Res_GETPACKAGE(resID) + 1; 121 //return mPackageMap[Res_GETPACKAGE(resID)+1]-1; 122 } 123 getResourcePackageIndexFromPackage(byte packageID)124 int getResourcePackageIndexFromPackage(byte packageID) { 125 return ((int)mPackageMap[packageID])-1; 126 } 127 128 // Errors add(final Object data, int size, final int cookie, boolean copyData) { 129 // return addInternal(data, size, NULL, 0, false, cookie, copyData); 130 // } 131 // 132 // Errors add(final Object data, int size, final Object idmapData, int idmapDataSize, 133 // final int cookie, boolean copyData, boolean appAsLib) { 134 // return addInternal(data, size, idmapData, idmapDataSize, appAsLib, cookie, copyData); 135 // } 136 // 137 // Errors add(Asset asset, final int cookie, boolean copyData) { 138 // final Object data = asset.getBuffer(true); 139 // if (data == NULL) { 140 // ALOGW("Unable to get buffer of resource asset file"); 141 // return UNKNOWN_ERROR; 142 // } 143 // 144 // return addInternal(data, static_cast<int>(asset.getLength()), NULL, false, 0, cookie, 145 // copyData); 146 // } 147 148 // status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false, 149 // bool appAsLib=false, bool isSystemAsset=false); add( Asset asset, Asset idmapAsset, final int cookie, boolean copyData, boolean appAsLib, boolean isSystemAsset)150 int add( 151 Asset asset, Asset idmapAsset, final int cookie, boolean copyData, 152 boolean appAsLib, boolean isSystemAsset) { 153 final byte[] data = asset.getBuffer(true); 154 if (data == NULL) { 155 ALOGW("Unable to get buffer of resource asset file"); 156 return UNKNOWN_ERROR; 157 } 158 159 int idmapSize = 0; 160 Object idmapData = NULL; 161 if (idmapAsset != NULL) { 162 idmapData = idmapAsset.getBuffer(true); 163 if (idmapData == NULL) { 164 ALOGW("Unable to get buffer of idmap asset file"); 165 return UNKNOWN_ERROR; 166 } 167 idmapSize = (int) idmapAsset.getLength(); 168 } 169 170 return addInternal(data, (int) asset.getLength(), 171 idmapData, idmapSize, appAsLib, cookie, copyData, isSystemAsset); 172 } 173 add(ResTable src, boolean isSystemAsset)174 int add(ResTable src, boolean isSystemAsset) 175 { 176 mError = src.mError; 177 178 for (int i=0; i < src.mHeaders.size(); i++) { 179 mHeaders.add(src.mHeaders.get(i)); 180 } 181 182 for (PackageGroup srcPg : src.mPackageGroups.values()) { 183 PackageGroup pg = new PackageGroup(this, srcPg.name, srcPg.id, 184 false /* appAsLib */, isSystemAsset || srcPg.isSystemAsset, srcPg.isDynamic); 185 for (int j=0; j<srcPg.packages.size(); j++) { 186 pg.packages.add(srcPg.packages.get(j)); 187 } 188 189 for (Integer typeId : srcPg.types.keySet()) { 190 List<Type> typeList = computeIfAbsent(pg.types, typeId, key -> new ArrayList<>()); 191 typeList.addAll(srcPg.types.get(typeId)); 192 } 193 pg.dynamicRefTable.addMappings(srcPg.dynamicRefTable); 194 pg.largestTypeId = max(pg.largestTypeId, srcPg.largestTypeId); 195 mPackageGroups.put(pg.id, pg); 196 } 197 198 // memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap)); 199 System.arraycopy(src.mPackageMap, 0, mPackageMap, 0, mPackageMap.length); 200 201 return mError; 202 } 203 addEmpty(final int cookie)204 int addEmpty(final int cookie) { 205 Header header = new Header(this); 206 header.index = mHeaders.size(); 207 header.cookie = cookie; 208 header.values.setToEmpty(); 209 header.ownedData = new byte[ResTable_header.SIZEOF]; 210 211 ByteBuffer buf = ByteBuffer.wrap(header.ownedData).order(ByteOrder.LITTLE_ENDIAN); 212 ResChunk_header.write(buf, (short) RES_TABLE_TYPE, () -> {}, () -> {}); 213 214 ResTable_header resHeader = new ResTable_header(buf, 0); 215 // resHeader.header.type = RES_TABLE_TYPE; 216 // resHeader.header.headerSize = sizeof(ResTable_header); 217 // resHeader.header.size = sizeof(ResTable_header); 218 219 header.header = resHeader; 220 mHeaders.add(header); 221 return (mError=NO_ERROR); 222 } 223 224 // status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize, 225 // bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset=false); addInternal(byte[] data, int dataSize, final Object idmapData, int idmapDataSize, boolean appAsLib, final int cookie, boolean copyData, boolean isSystemAsset)226 int addInternal(byte[] data, int dataSize, final Object idmapData, int idmapDataSize, 227 boolean appAsLib, final int cookie, boolean copyData, boolean isSystemAsset) 228 { 229 if (!isTruthy(data)) { 230 return NO_ERROR; 231 } 232 233 if (dataSize < ResTable_header.SIZEOF) { 234 ALOGE("Invalid data. Size(%d) is smaller than a ResTable_header(%d).", 235 (int) dataSize, (int) ResTable_header.SIZEOF); 236 return UNKNOWN_ERROR; 237 } 238 239 Header header = new Header(this); 240 header.index = mHeaders.size(); 241 header.cookie = cookie; 242 if (idmapData != NULL) { 243 header.resourceIDMap = new int[idmapDataSize / 4]; 244 if (header.resourceIDMap == NULL) { 245 // delete header; 246 return (mError = NO_MEMORY); 247 } 248 // memcpy(header.resourceIDMap, idmapData, idmapDataSize); 249 // header.resourceIDMapSize = idmapDataSize; 250 } 251 mHeaders.add(header); 252 253 final boolean notDeviceEndian = htods((short) 0xf0) != 0xf0; 254 255 if (kDebugLoadTableNoisy) { 256 ALOGV("Adding resources to ResTable: data=%s, size=0x%x, cookie=%d, copy=%b " + 257 "idmap=%s\n", data, dataSize, cookie, copyData, idmapData); 258 } 259 260 if (copyData || notDeviceEndian) { 261 header.ownedData = data; // malloc(dataSize); 262 if (header.ownedData == NULL) { 263 return (mError=NO_MEMORY); 264 } 265 // memcpy(header.ownedData, data, dataSize); 266 data = header.ownedData; 267 } 268 269 ByteBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); 270 // header->header = (const ResTable_header*)data; 271 header.header = new ResTable_header(buf, 0); 272 header.size = dtohl(header.header.header.size); 273 if (kDebugLoadTableSuperNoisy) { 274 ALOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header.size, 275 dtohl(header.header.header.size), header.header.header.size); 276 } 277 if (kDebugLoadTableNoisy) { 278 ALOGV("Loading ResTable @%s:\n", header.header); 279 } 280 if (dtohs(header.header.header.headerSize) > header.size 281 || header.size > dataSize) { 282 ALOGW( 283 "Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", 284 (int) dtohs(header.header.header.headerSize), (int) header.size, (int) dataSize); 285 return (mError=BAD_TYPE); 286 } 287 if (((dtohs(header.header.header.headerSize)|header.size)&0x3) != 0) { 288 ALOGW( 289 "Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n", 290 (int) dtohs(header.header.header.headerSize), (int) header.size); 291 return (mError=BAD_TYPE); 292 } 293 // header->dataEnd = ((const uint8_t*)header->header) + header->size; 294 header.dataEnd = header.size; 295 296 // Iterate through all chunks. 297 int curPackage = 0; 298 299 // const ResChunk_header* chunk = 300 // (const ResChunk_header*)(((const uint8_t*)header->header) 301 // + dtohs(header->header->header.headerSize)); 302 ResChunk_header chunk = 303 new ResChunk_header(buf, dtohs(header.header.header.headerSize)); 304 while (chunk != null 305 && (chunk.myOffset() <= (header.dataEnd - ResChunk_header.SIZEOF) 306 && chunk.myOffset() <= (header.dataEnd - dtohl(chunk.size)))) { 307 int err = validate_chunk(chunk, ResChunk_header.SIZEOF, header.dataEnd, "ResTable"); 308 if (err != NO_ERROR) { 309 return (mError=err); 310 } 311 if (kDebugTableNoisy) { 312 ALOGV( 313 "Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%s\n", 314 dtohs(chunk.type), 315 dtohs(chunk.headerSize), 316 dtohl(chunk.size), 317 (Object) (chunk.myOffset() - header.header.myOffset())); 318 } 319 final int csize = dtohl(chunk.size); 320 final int ctype = dtohs(chunk.type); 321 if (ctype == RES_STRING_POOL_TYPE) { 322 if (header.values.getError() != NO_ERROR) { 323 // Only use the first string chunk; ignore any others that 324 // may appear. 325 err = header.values.setTo(chunk.myBuf(), chunk.myOffset(), csize, false); 326 if (err != NO_ERROR) { 327 return (mError=err); 328 } 329 } else { 330 ALOGW("Multiple string chunks found in resource table."); 331 } 332 } else if (ctype == RES_TABLE_PACKAGE_TYPE) { 333 if (curPackage >= dtohl(header.header.packageCount)) { 334 ALOGW("More package chunks were found than the %d declared in the header.", 335 dtohl(header.header.packageCount)); 336 return (mError=BAD_TYPE); 337 } 338 339 if (parsePackage( 340 new ResTable_package(chunk.myBuf(), chunk.myOffset()), header, appAsLib, isSystemAsset) != NO_ERROR) { 341 return mError; 342 } 343 curPackage++; 344 } else { 345 ALOGW( 346 "Unknown chunk type 0x%x in table at 0x%x.\n", 347 ctype, chunk.myOffset() - header.header.myOffset()); 348 } 349 chunk = chunk.myOffset() + csize < header.dataEnd 350 ? new ResChunk_header(chunk.myBuf(), chunk.myOffset() + csize) 351 : null; 352 } 353 354 if (curPackage < dtohl(header.header.packageCount)) { 355 ALOGW("Fewer package chunks (%d) were found than the %d declared in the header.", 356 (int)curPackage, dtohl(header.header.packageCount)); 357 return (mError=BAD_TYPE); 358 } 359 mError = header.values.getError(); 360 if (mError != NO_ERROR) { 361 ALOGW("No string values found in resource table!"); 362 } 363 364 if (kDebugTableNoisy) { 365 ALOGV("Returning from add with mError=%d\n", mError); 366 } 367 return mError; 368 } 369 370 public final int getResource(int resID, Ref<Res_value> outValue, boolean mayBeBag, int density, 371 final Ref<Integer> outSpecFlags, Ref<ResTable_config> outConfig) 372 { 373 if (mError != NO_ERROR) { 374 return mError; 375 } 376 final int p = getResourcePackageIndex(resID); 377 final int t = Res_GETTYPE(resID); 378 final int e = Res_GETENTRY(resID); 379 if (p < 0) { 380 if (Res_GETPACKAGE(resID)+1 == 0) { 381 ALOGW("No package identifier when getting value for resource number 0x%08x", resID); 382 } else { 383 ALOGW("No known package when getting value for resource number 0x%08x", resID); 384 } 385 return BAD_INDEX; 386 } 387 388 if (t < 0) { 389 ALOGW("No type identifier when getting value for resource number 0x%08x", resID); 390 return BAD_INDEX; 391 } 392 final PackageGroup grp = mPackageGroups.get(p); 393 if (grp == NULL) { 394 ALOGW("Bad identifier when getting value for resource number 0x%08x", resID); 395 return BAD_INDEX; 396 } 397 // Allow overriding density 398 ResTable_config desiredConfig = mParams; 399 if (density > 0) { 400 desiredConfig.density = density; 401 } 402 Entry entry = new Entry(); 403 int err = getEntry(grp, t, e, desiredConfig, entry); 404 if (err != NO_ERROR) { 405 // Only log the failure when we're not running on the host as 406 // part of a tool. The caller will do its own logging. 407 return err; 408 } 409 410 if ((entry.entry.flags & ResTable_entry.FLAG_COMPLEX) != 0) { 411 if (!mayBeBag) { 412 ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID); 413 } 414 return BAD_VALUE; 415 } 416 417 // const Res_value* value = reinterpret_cast<const Res_value*>( 418 // reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size); 419 Res_value value = new Res_value(entry.entry.myBuf(), entry.entry.myOffset() + entry.entry.size); 420 421 // outValue.size = dtohs(value.size); 422 // outValue.res0 = value.res0; 423 // outValue.dataType = value.dataType; 424 // outValue.data = dtohl(value.data); 425 outValue.set(value); 426 427 // The reference may be pointing to a resource in a shared library. These 428 // references have build-time generated package IDs. These ids may not match 429 // the actual package IDs of the corresponding packages in this ResTable. 430 // We need to fix the package ID based on a mapping. 431 if (grp.dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) { 432 ALOGW("Failed to resolve referenced package: 0x%08x", outValue.get().data); 433 return BAD_VALUE; 434 } 435 436 // if (kDebugTableNoisy) { 437 // size_t len; 438 // printf("Found value: pkg=0x%x, type=%d, str=%s, int=%d\n", 439 // entry.package.header.index, 440 // outValue.dataType, 441 // outValue.dataType == Res_value::TYPE_STRING ? 442 // String8(entry.package.header.values.stringAt(outValue.data, &len)).string() : 443 // "", 444 // outValue.data); 445 // } 446 447 if (outSpecFlags != null) { 448 outSpecFlags.set(entry.specFlags); 449 } 450 if (outConfig != null) { 451 outConfig.set(entry.config); 452 } 453 return entry._package_.header.index; 454 } 455 456 public final int resolveReference(Ref<Res_value> value, int blockIndex, 457 final Ref<Integer> outLastRef) { 458 return resolveReference(value, blockIndex, outLastRef, null, null); 459 } 460 461 public final int resolveReference(Ref<Res_value> value, int blockIndex, 462 final Ref<Integer> outLastRef, Ref<Integer> inoutTypeSpecFlags) { 463 return resolveReference(value, blockIndex, outLastRef, inoutTypeSpecFlags, null); 464 } 465 466 public final int resolveReference(Ref<Res_value> value, int blockIndex, 467 final Ref<Integer> outLastRef, Ref<Integer> inoutTypeSpecFlags, 468 final Ref<ResTable_config> outConfig) 469 { 470 int count=0; 471 while (blockIndex >= 0 && value.get().dataType == DataType.REFERENCE.code() 472 && value.get().data != 0 && count < 20) { 473 if (outLastRef != null) { 474 outLastRef.set(value.get().data); 475 } 476 final Ref<Integer> newFlags = new Ref<>(0); 477 final int newIndex = getResource(value.get().data, value, true, 0, 478 newFlags, outConfig); 479 if (newIndex == BAD_INDEX) { 480 return BAD_INDEX; 481 } 482 if (kDebugTableTheme) { 483 ALOGI("Resolving reference 0x%x: newIndex=%d, type=0x%x, data=0x%x\n", 484 value.get().data, (int)newIndex, (int)value.get().dataType, value.get().data); 485 } 486 //printf("Getting reference 0x%08x: newIndex=%d\n", value.data, newIndex); 487 if (inoutTypeSpecFlags != null) { 488 inoutTypeSpecFlags.set(inoutTypeSpecFlags.get() | newFlags.get()); 489 } 490 if (newIndex < 0) { 491 // This can fail if the resource being referenced is a style... 492 // in this case, just return the reference, and expect the 493 // caller to deal with. 494 return blockIndex; 495 } 496 blockIndex = newIndex; 497 count++; 498 } 499 return blockIndex; 500 } 501 502 private interface Compare { 503 boolean compare(ResTable_sparseTypeEntry a, ResTable_sparseTypeEntry b); 504 } 505 506 ResTable_sparseTypeEntry lower_bound(ResTable_sparseTypeEntry first, ResTable_sparseTypeEntry last, 507 ResTable_sparseTypeEntry value, 508 Compare comparator) { 509 int count = (last.myOffset() - first.myOffset()) / ResTable_sparseTypeEntry.SIZEOF; 510 int itOffset; 511 int step; 512 while (count > 0) { 513 itOffset = first.myOffset(); 514 step = count / 2; 515 itOffset += step * ResTable_sparseTypeEntry.SIZEOF; 516 if (comparator.compare(new ResTable_sparseTypeEntry(first.myBuf(), itOffset), value)) { 517 itOffset += ResTable_sparseTypeEntry.SIZEOF; 518 first = new ResTable_sparseTypeEntry(first.myBuf(), itOffset); 519 } else { 520 count = step; 521 } 522 } 523 return first; 524 } 525 526 527 private int getEntry( 528 final PackageGroup packageGroup, int typeIndex, int entryIndex, 529 final ResTable_config config, 530 Entry outEntry) 531 { 532 final List<Type> typeList = getOrDefault(packageGroup.types, typeIndex, Collections.emptyList()); 533 if (typeList.isEmpty()) { 534 ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); 535 return BAD_TYPE; 536 } 537 538 ResTable_type bestType = null; 539 int bestOffset = ResTable_type.NO_ENTRY; 540 ResTablePackage bestPackage = null; 541 int specFlags = 0; 542 byte actualTypeIndex = (byte) typeIndex; 543 ResTable_config bestConfig = null; 544 // memset(&bestConfig, 0, sizeof(bestConfig)); 545 546 // Iterate over the Types of each package. 547 final int typeCount = typeList.size(); 548 for (int i = 0; i < typeCount; i++) { 549 final Type typeSpec = typeList.get(i); 550 551 int realEntryIndex = entryIndex; 552 int realTypeIndex = typeIndex; 553 boolean currentTypeIsOverlay = false; 554 555 // Runtime overlay packages provide a mapping of app resource 556 // ID to package resource ID. 557 if (typeSpec.idmapEntries.hasEntries()) { 558 final Ref<Short> overlayEntryIndex = new Ref<>((short) 0); 559 if (typeSpec.idmapEntries.lookup(entryIndex, overlayEntryIndex) != NO_ERROR) { 560 // No such mapping exists 561 continue; 562 } 563 realEntryIndex = overlayEntryIndex.get(); 564 realTypeIndex = typeSpec.idmapEntries.overlayTypeId() - 1; 565 currentTypeIsOverlay = true; 566 } 567 568 // Check that the entry idx is within range of the declared entry count (ResTable_typeSpec). 569 // Particular types (ResTable_type) may be encoded with sparse entries, and so their 570 // entryCount do not need to match. 571 if (((int) realEntryIndex) >= typeSpec.entryCount) { 572 ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)", 573 Res_MAKEID(packageGroup.id - 1, typeIndex, entryIndex), 574 entryIndex, ((int) typeSpec.entryCount)); 575 // We should normally abort here, but some legacy apps declare 576 // resources in the 'android' package (old bug in AAPT). 577 continue; 578 } 579 580 // Aggregate all the flags for each package that defines this entry. 581 if (typeSpec.typeSpecFlags != null) { 582 specFlags |= dtohl(typeSpec.typeSpecFlags[realEntryIndex]); 583 } else { 584 specFlags = -1; 585 } 586 587 List<ResTable_type> candidateConfigs = typeSpec.configs; 588 589 // List<ResTable_type> filteredConfigs; 590 // if (isTruthy(config) && Objects.equals(mParams, config)) { 591 // // Grab the lock first so we can safely get the current filtered list. 592 // synchronized (mFilteredConfigLock) { 593 // // This configuration is equal to the one we have previously cached for, 594 // // so use the filtered configs. 595 // 596 // final TypeCacheEntry cacheEntry = packageGroup.typeCacheEntries.get(typeIndex); 597 // if (i < cacheEntry.filteredConfigs.size()) { 598 // if (isTruthy(cacheEntry.filteredConfigs.get(i))) { 599 // // Grab a reference to the shared_ptr so it doesn't get destroyed while 600 // // going through this list. 601 // filteredConfigs = cacheEntry.filteredConfigs.get(i); 602 // 603 // // Use this filtered list. 604 // candidateConfigs = filteredConfigs; 605 // } 606 // } 607 // } 608 // } 609 610 final int numConfigs = candidateConfigs.size(); 611 for (int c = 0; c < numConfigs; c++) { 612 final ResTable_type thisType = candidateConfigs.get(c); 613 if (thisType == NULL) { 614 continue; 615 } 616 617 final ResTable_config thisConfig; 618 // thisConfig.copyFromDtoH(thisType.config); 619 thisConfig = ResTable_config.fromDtoH(thisType.config); 620 621 // Check to make sure this one is valid for the current parameters. 622 if (config != NULL && !thisConfig.match(config)) { 623 continue; 624 } 625 626 // const uint32_t* const eindex = reinterpret_cast<const uint32_t*>( 627 // reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize)); 628 629 final int eindex = thisType.myOffset() + dtohs(thisType.header.headerSize); 630 631 int thisOffset; 632 633 // Check if there is the desired entry in this type. 634 if (isTruthy(thisType.flags & ResTable_type.FLAG_SPARSE)) { 635 // This is encoded as a sparse map, so perform a binary search. 636 final ByteBuffer buf = thisType.myBuf(); 637 ResTable_sparseTypeEntry sparseIndices = new ResTable_sparseTypeEntry(buf, eindex); 638 ResTable_sparseTypeEntry result = 639 lower_bound( 640 sparseIndices, 641 new ResTable_sparseTypeEntry( 642 buf, sparseIndices.myOffset() + dtohl(thisType.entryCount)), 643 new ResTable_sparseTypeEntry(buf, realEntryIndex), 644 (a, b) -> dtohs(a.idx) < dtohs(b.idx)); 645 // if (result == sparseIndices + dtohl(thisType.entryCount) 646 // || dtohs(result.idx) != realEntryIndex) { 647 if (result.myOffset() == sparseIndices.myOffset() + dtohl(thisType.entryCount) 648 || dtohs(result.idx) != realEntryIndex) { 649 // No entry found. 650 continue; 651 } 652 // Extract the offset from the entry. Each offset must be a multiple of 4 653 // so we store it as the real offset divided by 4. 654 // thisOffset = dtohs(result->offset) * 4u; 655 thisOffset = dtohs(result.offset) * 4; 656 } else { 657 if (realEntryIndex >= dtohl(thisType.entryCount)) { 658 // Entry does not exist. 659 continue; 660 } 661 // thisOffset = dtohl(eindex[realEntryIndex]); 662 thisOffset = thisType.entryOffset(realEntryIndex); 663 } 664 665 if (thisOffset == ResTable_type.NO_ENTRY) { 666 // There is no entry for this index and configuration. 667 continue; 668 } 669 670 if (bestType != NULL) { 671 // Check if this one is less specific than the last found. If so, 672 // we will skip it. We check starting with things we most care 673 // about to those we least care about. 674 if (!thisConfig.isBetterThan(bestConfig, config)) { 675 if (!currentTypeIsOverlay || thisConfig.compare(bestConfig) != 0) { 676 continue; 677 } 678 } 679 } 680 681 bestType = thisType; 682 bestOffset = thisOffset; 683 bestConfig = thisConfig; 684 bestPackage = typeSpec._package_; 685 actualTypeIndex = (byte) realTypeIndex; 686 687 // If no config was specified, any type will do, so skip 688 if (config == NULL) { 689 break; 690 } 691 } 692 } 693 694 if (bestType == NULL) { 695 return BAD_INDEX; 696 } 697 698 bestOffset += dtohl(bestType.entriesStart); 699 700 // if (bestOffset > (dtohl(bestType->header.size)-sizeof(ResTable_entry))) { 701 if (bestOffset > (dtohl(bestType.header.size)- ResTable_entry.SIZEOF)) { 702 ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", 703 bestOffset, dtohl(bestType.header.size)); 704 return BAD_TYPE; 705 } 706 if ((bestOffset & 0x3) != 0) { 707 ALOGW("ResTable_entry at 0x%x is not on an integer boundary", bestOffset); 708 return BAD_TYPE; 709 } 710 711 // const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>( 712 // reinterpret_cast<const uint8_t*>(bestType) + bestOffset); 713 final ResTable_entry entry = new ResTable_entry(bestType.myBuf(), 714 bestType.myOffset() + bestOffset); 715 int entrySize = entry.isCompact() ? ResTable_entry.SIZEOF : dtohs(entry.size); 716 if (entrySize < ResTable_entry.SIZEOF) { 717 ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry.size)); 718 return BAD_TYPE; 719 } 720 721 if (outEntry != null) { 722 outEntry.entry = entry; 723 outEntry.config = bestConfig; 724 outEntry.type = bestType; 725 outEntry.specFlags = specFlags; 726 outEntry._package_ = bestPackage; 727 outEntry.typeStr = new StringPoolRef(bestPackage.typeStrings, actualTypeIndex - bestPackage.typeIdOffset); 728 outEntry.keyStr = new StringPoolRef(bestPackage.keyStrings, dtohl(entry.getKeyIndex())); 729 } 730 return NO_ERROR; 731 } 732 733 int parsePackage(ResTable_package pkg, 734 Header header, boolean appAsLib, boolean isSystemAsset) 735 { 736 int base = pkg.myOffset(); 737 int err = validate_chunk(pkg.header, ResTable_package.SIZEOF - 4 /*sizeof(pkg.typeIdOffset)*/, 738 header.dataEnd, "ResTable_package"); 739 if (err != NO_ERROR) { 740 return (mError=err); 741 } 742 743 final int pkgSize = dtohl(pkg.header.size); 744 745 if (dtohl(pkg.typeStrings) >= pkgSize) { 746 ALOGW("ResTable_package type strings at 0x%x are past chunk size 0x%x.", 747 dtohl(pkg.typeStrings), pkgSize); 748 return (mError=BAD_TYPE); 749 } 750 if ((dtohl(pkg.typeStrings)&0x3) != 0) { 751 ALOGW("ResTable_package type strings at 0x%x is not on an integer boundary.", 752 dtohl(pkg.typeStrings)); 753 return (mError=BAD_TYPE); 754 } 755 if (dtohl(pkg.keyStrings) >= pkgSize) { 756 ALOGW("ResTable_package key strings at 0x%x are past chunk size 0x%x.", 757 dtohl(pkg.keyStrings), pkgSize); 758 return (mError=BAD_TYPE); 759 } 760 if ((dtohl(pkg.keyStrings)&0x3) != 0) { 761 ALOGW("ResTable_package key strings at 0x%x is not on an integer boundary.", 762 dtohl(pkg.keyStrings)); 763 return (mError=BAD_TYPE); 764 } 765 766 int id = dtohl(pkg.id); 767 final Map<Byte, IdmapEntries> idmapEntries = new HashMap<>(); 768 769 if (header.resourceIDMap != NULL) { 770 // byte targetPackageId = 0; 771 // int err = parseIdmap(header.resourceIDMap, header.resourceIDMapSize, &targetPackageId, &idmapEntries); 772 // if (err != NO_ERROR) { 773 // ALOGW("Overlay is broken"); 774 // return (mError=err); 775 // } 776 // id = targetPackageId; 777 } 778 779 boolean isDynamic = false; 780 if (id >= 256) { 781 // LOG_ALWAYS_FATAL("Package id out of range"); 782 throw new IllegalStateException("Package id out of range"); 783 // return NO_ERROR; 784 } else if (id == 0 || (id == 0x7f && appAsLib) || isSystemAsset) { 785 // This is a library or a system asset, so assign an ID 786 id = mNextPackageId++; 787 isDynamic = true; 788 } 789 790 PackageGroup group = null; 791 ResTablePackage _package = new ResTablePackage(this, header, pkg); 792 if (_package == NULL) { 793 return (mError=NO_MEMORY); 794 } 795 796 // err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), 797 // header->dataEnd-(base+dtohl(pkg->typeStrings))); 798 err = _package.typeStrings.setTo(pkg.myBuf(), base+dtohl(pkg.typeStrings), 799 header.dataEnd -(base+dtohl(pkg.typeStrings)), false); 800 if (err != NO_ERROR) { 801 // delete group; 802 // delete _package; 803 return (mError=err); 804 } 805 806 // err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), 807 // header->dataEnd-(base+dtohl(pkg->keyStrings))); 808 err = _package.keyStrings.setTo(pkg.myBuf(), base+dtohl(pkg.keyStrings), 809 header.dataEnd -(base+dtohl(pkg.keyStrings)), false); 810 if (err != NO_ERROR) { 811 // delete group; 812 // delete _package; 813 return (mError=err); 814 } 815 816 int idx = mPackageMap[id]; 817 if (idx == 0) { 818 idx = mPackageGroups.size() + 1; 819 820 // char[] tmpName = new char[pkg.name.length /*sizeof(pkg.name)/sizeof(pkg.name[0])*/]; 821 // strcpy16_dtoh(tmpName, pkg.name, sizeof(pkg.name)/sizeof(pkg.name[0])); 822 group = new PackageGroup(this, new String(pkg.name), id, appAsLib, isSystemAsset, isDynamic); 823 if (group == NULL) { 824 // delete _package; 825 return (mError=NO_MEMORY); 826 } 827 828 mPackageGroups.put(group.id, group); 829 // if (err < NO_ERROR) { 830 // return (mError=err); 831 // } 832 833 mPackageMap[id] = (byte) idx; 834 835 // Find all packages that reference this package 836 // int N = mPackageGroups.size(); 837 // for (int i = 0; i < N; i++) { 838 for (PackageGroup packageGroup : mPackageGroups.values()) { 839 packageGroup.dynamicRefTable.addMapping( 840 group.name, (byte) group.id); 841 } 842 } else { 843 group = mPackageGroups.get(idx - 1); 844 if (group == NULL) { 845 return (mError=UNKNOWN_ERROR); 846 } 847 } 848 849 group.packages.add(_package); 850 // if (err < NO_ERROR) { 851 // return (mError=err); 852 // } 853 854 // Iterate through all chunks. 855 ResChunk_header chunk = 856 new ResChunk_header(pkg.myBuf(), pkg.myOffset() + dtohs(pkg.header.headerSize)); 857 // const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size); 858 final int endPos = pkg.myOffset() + pkg.header.size; 859 // while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) && 860 // ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) { 861 while (chunk != null 862 && chunk.myOffset() <= (endPos - ResChunk_header.SIZEOF) 863 && chunk.myOffset() <= (endPos - dtohl(chunk.size))) { 864 if (kDebugTableNoisy) { 865 ALOGV( 866 "PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%s\n", 867 dtohs(chunk.type), 868 dtohs(chunk.headerSize), 869 dtohl(chunk.size), 870 (chunk.myOffset() - header.header.myOffset())); 871 } 872 final int csize = dtohl(chunk.size); 873 final short ctype = dtohs(chunk.type); 874 if (ctype == RES_TABLE_TYPE_SPEC_TYPE) { 875 final ResTable_typeSpec typeSpec = new ResTable_typeSpec(chunk.myBuf(), chunk.myOffset()); 876 err = validate_chunk(typeSpec.header, ResTable_typeSpec.SIZEOF, 877 endPos, "ResTable_typeSpec"); 878 if (err != NO_ERROR) { 879 return (mError=err); 880 } 881 882 final int typeSpecSize = dtohl(typeSpec.header.size); 883 final int newEntryCount = dtohl(typeSpec.entryCount); 884 885 if (kDebugLoadTableNoisy) { 886 ALOGI("TypeSpec off %s: type=0x%x, headerSize=0x%x, size=%s\n", 887 (base-chunk.myOffset()), 888 dtohs(typeSpec.header.type), 889 dtohs(typeSpec.header.headerSize), 890 typeSpecSize); 891 } 892 // look for block overrun or int overflow when multiplying by 4 893 if ((dtohl(typeSpec.entryCount) > (Integer.MAX_VALUE/4 /*sizeof(int)*/) 894 || dtohs(typeSpec.header.headerSize)+(4 /*sizeof(int)*/*newEntryCount) 895 > typeSpecSize)) { 896 ALOGW("ResTable_typeSpec entry index to %s extends beyond chunk end %s.", 897 (dtohs(typeSpec.header.headerSize) + (4 /*sizeof(int)*/*newEntryCount)), 898 typeSpecSize); 899 return (mError=BAD_TYPE); 900 } 901 902 if (typeSpec.id == 0) { 903 ALOGW("ResTable_type has an id of 0."); 904 return (mError=BAD_TYPE); 905 } 906 907 if (newEntryCount > 0) { 908 boolean addToType = true; 909 byte typeIndex = (byte) (typeSpec.id - 1); 910 IdmapEntries idmapEntry = idmapEntries.get(typeSpec.id); 911 if (idmapEntry != null) { 912 typeIndex = (byte) (idmapEntry.targetTypeId() - 1); 913 } else if (header.resourceIDMap != NULL) { 914 // This is an overlay, but the types in this overlay are not 915 // overlaying anything according to the idmap. We can skip these 916 // as they will otherwise conflict with the other resources in the package 917 // without a mapping. 918 addToType = false; 919 } 920 921 if (addToType) { 922 List<Type> typeList = computeIfAbsent(group.types, (int) typeIndex, k -> new ArrayList<>()); 923 if (!typeList.isEmpty()) { 924 final Type existingType = typeList.get(0); 925 if (existingType.entryCount != newEntryCount && idmapEntry == null) { 926 ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", 927 (int) newEntryCount, (int) existingType.entryCount); 928 // We should normally abort here, but some legacy apps declare 929 // resources in the 'android' package (old bug in AAPT). 930 } 931 } 932 933 Type t = new Type(header, _package, newEntryCount); 934 t.typeSpec = typeSpec; 935 t.typeSpecFlags = typeSpec.getSpecFlags(); 936 if (idmapEntry != null) { 937 t.idmapEntries = idmapEntry; 938 } 939 typeList.add(t); 940 group.largestTypeId = max(group.largestTypeId, typeSpec.id); 941 } 942 } else { 943 ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec.id); 944 } 945 946 } else if (ctype == RES_TABLE_TYPE_TYPE) { 947 ResTable_type type = new ResTable_type(chunk.myBuf(), chunk.myOffset()); 948 err = validate_chunk(type.header, ResTable_type.SIZEOF_WITHOUT_CONFIG/*-sizeof(ResTable_config)*/+4, 949 endPos, "ResTable_type"); 950 if (err != NO_ERROR) { 951 return (mError=err); 952 } 953 954 final int typeSize = dtohl(type.header.size); 955 final int newEntryCount = dtohl(type.entryCount); 956 957 if (kDebugLoadTableNoisy) { 958 System.out.println(String.format("Type off 0x%x: type=0x%x, headerSize=0x%x, size=%d\n", 959 base-chunk.myOffset(), 960 dtohs(type.header.type), 961 dtohs(type.header.headerSize), 962 typeSize)); 963 } 964 // Check if the table uses compact encoding. 965 int bytesPerEntry = isTruthy(type.flags & ResTable_type.FLAG_OFFSET16) ? 2 : 4; 966 if (dtohs(type.header.headerSize) + (bytesPerEntry * newEntryCount) > typeSize) { 967 ALOGW( 968 "ResTable_type entry index to %s extends beyond chunk end 0x%x.", 969 (dtohs(type.header.headerSize) + (bytesPerEntry * newEntryCount)), typeSize); 970 return (mError=BAD_TYPE); 971 } 972 973 if (newEntryCount != 0 974 && dtohl(type.entriesStart) > (typeSize- ResTable_entry.SIZEOF)) { 975 ALOGW("ResTable_type entriesStart at 0x%x extends beyond chunk end 0x%x.", 976 dtohl(type.entriesStart), typeSize); 977 return (mError=BAD_TYPE); 978 } 979 980 if (type.id == 0) { 981 ALOGW("ResTable_type has an id of 0."); 982 return (mError=BAD_TYPE); 983 } 984 985 if (newEntryCount > 0) { 986 boolean addToType = true; 987 byte typeIndex = (byte) (type.id - 1); 988 IdmapEntries idmapEntry = idmapEntries.get(type.id); 989 if (idmapEntry != null) { 990 typeIndex = (byte) (idmapEntry.targetTypeId() - 1); 991 } else if (header.resourceIDMap != NULL) { 992 // This is an overlay, but the types in this overlay are not 993 // overlaying anything according to the idmap. We can skip these 994 // as they will otherwise conflict with the other resources in the package 995 // without a mapping. 996 addToType = false; 997 } 998 999 if (addToType) { 1000 List<Type> typeList = getOrDefault(group.types, (int) typeIndex, Collections.emptyList()); 1001 if (typeList.isEmpty()) { 1002 ALOGE("No TypeSpec for type %d", type.id); 1003 return (mError = BAD_TYPE); 1004 } 1005 1006 Type t = typeList.get(typeList.size() - 1); 1007 if (t._package_ != _package) { 1008 ALOGE("No TypeSpec for type %d", type.id); 1009 return (mError = BAD_TYPE); 1010 } 1011 1012 t.configs.add(type); 1013 1014 if (kDebugTableGetEntry) { 1015 ResTable_config thisConfig = ResTable_config.fromDtoH(type.config); 1016 ALOGI("Adding config to type %d: %s\n", type.id, 1017 thisConfig.toString()); 1018 } 1019 } 1020 } else { 1021 ALOGV("Skipping empty ResTable_type for type %d", type.id); 1022 } 1023 1024 } else if (ctype == RES_TABLE_LIBRARY_TYPE) { 1025 if (group.dynamicRefTable.entries().isEmpty()) { 1026 throw new UnsupportedOperationException("libraries not supported yet"); 1027 // const ResTable_lib_header* lib = (const ResTable_lib_header*) chunk; 1028 // status_t err = validate_chunk(&lib->header, sizeof(*lib), 1029 // endPos, "ResTable_lib_header"); 1030 // if (err != NO_ERROR) { 1031 // return (mError=err); 1032 // } 1033 // 1034 // err = group->dynamicRefTable.load(lib); 1035 // if (err != NO_ERROR) { 1036 // return (mError=err); 1037 // } 1038 // 1039 // // Fill in the reference table with the entries we already know about. 1040 // size_t N = mPackageGroups.size(); 1041 // for (size_t i = 0; i < N; i++) { 1042 // group.dynamicRefTable.addMapping(mPackageGroups[i].name, mPackageGroups[i].id); 1043 // } 1044 } else { 1045 ALOGW("Found multiple library tables, ignoring..."); 1046 } 1047 } else { 1048 err = validate_chunk(chunk, ResChunk_header.SIZEOF, 1049 endPos, "ResTable_package:unknown"); 1050 if (err != NO_ERROR) { 1051 return (mError=err); 1052 } 1053 } 1054 chunk = chunk.myOffset() + csize < endPos ? new ResChunk_header(chunk.myBuf(), chunk.myOffset() + csize) : null; 1055 } 1056 1057 return NO_ERROR; 1058 } 1059 1060 public int getTableCookie(int index) { 1061 return mHeaders.get(index).cookie; 1062 } 1063 1064 void setParameters(ResTable_config params) 1065 { 1066 // AutoMutex _lock(mLock); 1067 // AutoMutex _lock2(mFilteredConfigLock); 1068 synchronized (mLock) { 1069 synchronized (mFilteredConfigLock) { 1070 if (kDebugTableGetEntry) { 1071 ALOGI("Setting parameters: %s\n", params.toString()); 1072 } 1073 mParams = params; 1074 for (PackageGroup packageGroup : mPackageGroups.values()) { 1075 if (kDebugTableNoisy) { 1076 ALOGI("CLEARING BAGS FOR GROUP 0x%x!", packageGroup.id); 1077 } 1078 packageGroup.clearBagCache(); 1079 1080 // Find which configurations match the set of parameters. This allows for a much 1081 // faster lookup in getEntry() if the set of values is narrowed down. 1082 //for (int t = 0; t < packageGroup.types.size(); t++) { 1083 //if (packageGroup.types.get(t).isEmpty()) { 1084 // continue; 1085 // } 1086 // 1087 // List<Type> typeList = packageGroup.types.get(t); 1088 for (List<Type> typeList : packageGroup.types.values()) { 1089 if (typeList.isEmpty()) { 1090 continue; 1091 } 1092 1093 // Retrieve the cache entry for this type. 1094 //TypeCacheEntry cacheEntry = packageGroup.typeCacheEntries.editItemAt(t); 1095 1096 for (int ts = 0; ts < typeList.size(); ts++) { 1097 Type type = typeList.get(ts); 1098 1099 // std::shared_ptr<Vector<const ResTable_type*>> newFilteredConfigs = 1100 // std::make_shared<Vector<const ResTable_type*>>(); 1101 List<ResTable_type> newFilteredConfigs = new ArrayList<>(); 1102 1103 for (int ti = 0; ti < type.configs.size(); ti++) { 1104 ResTable_config config = ResTable_config.fromDtoH(type.configs.get(ti).config); 1105 1106 if (config.match(mParams)) { 1107 newFilteredConfigs.add(type.configs.get(ti)); 1108 } 1109 } 1110 1111 if (kDebugTableNoisy) { 1112 ALOGD("Updating pkg=0x%x type=0x%x with 0x%x filtered configs", 1113 packageGroup.id, ts, newFilteredConfigs.size()); 1114 } 1115 1116 // todo: implement cache 1117 // cacheEntry.filteredConfigs.add(newFilteredConfigs); 1118 } 1119 } 1120 } 1121 } 1122 } 1123 } 1124 1125 ResTable_config getParameters() 1126 { 1127 // mLock.lock(); 1128 synchronized (mLock) { 1129 return mParams; 1130 } 1131 // mLock.unlock(); 1132 } 1133 1134 private static final Map<String, Integer> sInternalNameToIdMap = new HashMap<>(); 1135 static { 1136 sInternalNameToIdMap.put("^type", ResTable_map.ATTR_TYPE); 1137 sInternalNameToIdMap.put("^l10n", ResTable_map.ATTR_L10N); 1138 sInternalNameToIdMap.put("^min" , ResTable_map.ATTR_MIN); 1139 sInternalNameToIdMap.put("^max", ResTable_map.ATTR_MAX); 1140 sInternalNameToIdMap.put("^other", ResTable_map.ATTR_OTHER); 1141 sInternalNameToIdMap.put("^zero", ResTable_map.ATTR_ZERO); 1142 sInternalNameToIdMap.put("^one", ResTable_map.ATTR_ONE); 1143 sInternalNameToIdMap.put("^two", ResTable_map.ATTR_TWO); 1144 sInternalNameToIdMap.put("^few", ResTable_map.ATTR_FEW); 1145 sInternalNameToIdMap.put("^many", ResTable_map.ATTR_MANY); 1146 } 1147 1148 public int identifierForName(String name, String type, String packageName) { 1149 return identifierForName(name, type, packageName, null); 1150 } 1151 1152 public int identifierForName(String nameString, String type, String packageName, 1153 final Ref<Integer> outTypeSpecFlags) { 1154 // if (kDebugTableSuperNoisy) { 1155 // printf("Identifier for name: error=%d\n", mError); 1156 // } 1157 // // Check for internal resource identifier as the very first thing, so 1158 // // that we will always find them even when there are no resources. 1159 if (nameString.startsWith("^")) { 1160 if (sInternalNameToIdMap.containsKey(nameString)) { 1161 if (outTypeSpecFlags != null) { 1162 outTypeSpecFlags.set(ResTable_typeSpec.SPEC_PUBLIC); 1163 } 1164 return sInternalNameToIdMap.get(nameString); 1165 } 1166 if (nameString.length() > 7) 1167 if (nameString.substring(1, 6).equals("index_")) { 1168 int index = Integer.getInteger(nameString.substring(7)); 1169 if (Res_CHECKID(index)) { 1170 ALOGW("Array resource index: %d is too large.", 1171 index); 1172 return 0; 1173 } 1174 if (outTypeSpecFlags != null) { 1175 outTypeSpecFlags.set(ResTable_typeSpec.SPEC_PUBLIC); 1176 } 1177 return Res_MAKEARRAY(index); 1178 } 1179 1180 return 0; 1181 } 1182 1183 if (mError != NO_ERROR) { 1184 return 0; 1185 } 1186 1187 1188 // Figure out the package and type we are looking in... 1189 // TODO(BC): The following code block was a best effort attempt to directly transliterate 1190 // C++ code which uses pointer artihmetic. Consider replacing with simpler logic 1191 1192 boolean fakePublic = false; 1193 char[] name = nameString.toCharArray(); 1194 int packageEnd = -1; 1195 int typeEnd = -1; 1196 int nameEnd = name.length; 1197 int pIndex = 0; 1198 while (pIndex < nameEnd) { 1199 char p = name[pIndex]; 1200 if (p == ':') packageEnd = pIndex; 1201 else if (p == '/') typeEnd = pIndex; 1202 pIndex++; 1203 } 1204 int nameIndex = 0; 1205 if (name[nameIndex] == '@') { 1206 nameIndex++; 1207 if (name[nameIndex] == '*') { 1208 fakePublic = true; 1209 nameIndex++; 1210 } 1211 } 1212 if (nameIndex >= nameEnd) { 1213 return 0; 1214 } 1215 if (packageEnd != -1) { 1216 packageName = nameString.substring(nameIndex, packageEnd); 1217 nameIndex = packageEnd+1; 1218 } else if (packageName == null) { 1219 return 0; 1220 } 1221 if (typeEnd != -1) { 1222 type = nameString.substring(nameIndex, typeEnd); 1223 nameIndex = typeEnd+1; 1224 } else if (type == null) { 1225 return 0; 1226 } 1227 if (nameIndex >= nameEnd) { 1228 return 0; 1229 } 1230 nameString = nameString.substring(nameIndex, nameEnd); 1231 1232 // nameLen = nameEnd-name; 1233 // if (kDebugTableNoisy) { 1234 // printf("Looking for identifier: type=%s, name=%s, package=%s\n", 1235 // String8(type, typeLen).string(), 1236 // String8(name, nameLen).string(), 1237 // String8(package, packageLen).string()); 1238 // } 1239 final String attr = "attr"; 1240 final String attrPrivate = "^attr-private"; 1241 for (PackageGroup group : mPackageGroups.values()) { 1242 if (!Objects.equals(packageName.trim(), group.name.trim())) { 1243 if (kDebugTableNoisy) { 1244 System.out.println(String.format("Skipping package group: %s\n", group.name)); 1245 } 1246 continue; 1247 } 1248 for (ResTablePackage pkg : group.packages) { 1249 String targetType = type; 1250 1251 do { 1252 int ti = pkg.typeStrings.indexOfString(targetType); 1253 if (ti < 0) { 1254 continue; 1255 } 1256 ti += pkg.typeIdOffset; 1257 int identifier = findEntry(group, ti, nameString, outTypeSpecFlags); 1258 if (identifier != 0) { 1259 if (fakePublic && outTypeSpecFlags != null) { 1260 outTypeSpecFlags.set(outTypeSpecFlags.get() | ResTable_typeSpec.SPEC_PUBLIC); 1261 } 1262 return identifier; 1263 } 1264 } while (attr.compareTo(targetType) == 0 1265 && ((targetType = attrPrivate) != null) 1266 ); 1267 } 1268 break; 1269 } 1270 return 0; 1271 } 1272 1273 int findEntry(PackageGroup group, int typeIndex, String name, Ref<Integer> outTypeSpecFlags) { 1274 // const TypeList& typeList = group->types[typeIndex]; 1275 List<Type> typeList = getOrDefault(group.types, typeIndex, Collections.emptyList()); 1276 // const size_t typeCount = typeList.size(); 1277 // for (size_t i = 0; i < typeCount; i++) { 1278 for (Type type : typeList) { 1279 // const Type* t = typeList[i]; 1280 // const base::expected<size_t, NullOrIOError> ei = 1281 // t->package->keyStrings.indexOfString(name, nameLen); 1282 int ei = type._package_.keyStrings.indexOfString(name); 1283 // if (!ei.has_value()) { 1284 if (ei < 0) { 1285 continue; 1286 } 1287 // const size_t configCount = t->configs.size(); 1288 // for (size_t j = 0; j < configCount; j++) { 1289 for (ResTable_type resTableType : type.configs) { 1290 // const TypeVariant tv(t->configs[j]); 1291 // for (TypeVariant::iterator iter = tv.beginEntries(); 1292 // iter != tv.endEntries(); 1293 // iter++) { 1294 // const ResTable_entry* entry = *iter; 1295 // if (entry == NULL) { 1296 // continue; 1297 // } 1298 int entryIndex = resTableType.findEntryByResName(ei); 1299 if (entryIndex >= 0) { 1300 int resId = Res_MAKEID(group.id - 1, typeIndex, entryIndex); 1301 if (outTypeSpecFlags != null) { 1302 Entry result = new Entry(); 1303 if (getEntry(group, typeIndex, entryIndex, null, result) != NO_ERROR) { 1304 ALOGW("Failed to find spec flags for 0x%08x", resId); 1305 return 0; 1306 } 1307 outTypeSpecFlags.set(result.specFlags); 1308 } 1309 return resId; 1310 } 1311 } 1312 } 1313 return 0; 1314 } 1315 1316 //bool ResTable::expandResourceRef(const char16_t* refStr, size_t refLen, 1317 // String16* outPackage, 1318 // String16* outType, 1319 // String16* outName, 1320 // const String16* defType, 1321 // const String16* defPackage, 1322 // const char** outErrorMsg, 1323 // bool* outPublicOnly) 1324 //{ 1325 // const char16_t* packageEnd = NULL; 1326 // const char16_t* typeEnd = NULL; 1327 // const char16_t* p = refStr; 1328 // const char16_t* const end = p + refLen; 1329 // while (p < end) { 1330 // if (*p == ':') packageEnd = p; 1331 // else if (*p == '/') { 1332 // typeEnd = p; 1333 // break; 1334 // } 1335 // p++; 1336 // } 1337 // p = refStr; 1338 // if (*p == '@') p++; 1339 // 1340 // if (outPublicOnly != NULL) { 1341 // *outPublicOnly = true; 1342 // } 1343 // if (*p == '*') { 1344 // p++; 1345 // if (outPublicOnly != NULL) { 1346 // *outPublicOnly = false; 1347 // } 1348 // } 1349 // 1350 // if (packageEnd) { 1351 // *outPackage = String16(p, packageEnd-p); 1352 // p = packageEnd+1; 1353 // } else { 1354 // if (!defPackage) { 1355 // if (outErrorMsg) { 1356 // *outErrorMsg = "No resource package specified"; 1357 // } 1358 // return false; 1359 // } 1360 // *outPackage = *defPackage; 1361 // } 1362 // if (typeEnd) { 1363 // *outType = String16(p, typeEnd-p); 1364 // p = typeEnd+1; 1365 // } else { 1366 // if (!defType) { 1367 // if (outErrorMsg) { 1368 // *outErrorMsg = "No resource type specified"; 1369 // } 1370 // return false; 1371 // } 1372 // *outType = *defType; 1373 // } 1374 // *outName = String16(p, end-p); 1375 // if(**outPackage == 0) { 1376 // if(outErrorMsg) { 1377 // *outErrorMsg = "Resource package cannot be an empty string"; 1378 // } 1379 // return false; 1380 // } 1381 // if(**outType == 0) { 1382 // if(outErrorMsg) { 1383 // *outErrorMsg = "Resource type cannot be an empty string"; 1384 // } 1385 // return false; 1386 // } 1387 // if(**outName == 0) { 1388 // if(outErrorMsg) { 1389 // *outErrorMsg = "Resource id cannot be an empty string"; 1390 // } 1391 // return false; 1392 // } 1393 // return true; 1394 //} 1395 // 1396 //static uint32_t get_hex(char c, bool* outError) 1397 //{ 1398 // if (c >= '0' && c <= '9') { 1399 // return c - '0'; 1400 // } else if (c >= 'a' && c <= 'f') { 1401 // return c - 'a' + 0xa; 1402 // } else if (c >= 'A' && c <= 'F') { 1403 // return c - 'A' + 0xa; 1404 // } 1405 // *outError = true; 1406 // return 0; 1407 //} 1408 // 1409 //struct unit_entry 1410 //{ 1411 // const char* name; 1412 // size_t len; 1413 // uint8_t type; 1414 // uint32_t unit; 1415 // float scale; 1416 //}; 1417 // 1418 //static const unit_entry unitNames[] = { 1419 // { "px", strlen("px"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PX, 1.0f }, 1420 // { "dip", strlen("dip"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, 1421 // { "dp", strlen("dp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, 1422 // { "sp", strlen("sp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_SP, 1.0f }, 1423 // { "pt", strlen("pt"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PT, 1.0f }, 1424 // { "in", strlen("in"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_IN, 1.0f }, 1425 // { "mm", strlen("mm"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_MM, 1.0f }, 1426 // { "%", strlen("%"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION, 1.0f/100 }, 1427 // { "%s", strlen("%s"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100 }, 1428 // { NULL, 0, 0, 0, 0 } 1429 //}; 1430 // 1431 //static bool parse_unit(const char* str, Res_value* outValue, 1432 // float* outScale, const char** outEnd) 1433 //{ 1434 // const char* end = str; 1435 // while (*end != 0 && !isspace((unsigned char)*end)) { 1436 // end++; 1437 // } 1438 // const size_t len = end-str; 1439 // 1440 // const char* realEnd = end; 1441 // while (*realEnd != 0 && isspace((unsigned char)*realEnd)) { 1442 // realEnd++; 1443 // } 1444 // if (*realEnd != 0) { 1445 // return false; 1446 // } 1447 // 1448 // const unit_entry* cur = unitNames; 1449 // while (cur->name) { 1450 // if (len == cur->len && strncmp(cur->name, str, len) == 0) { 1451 // outValue->dataType = cur->type; 1452 // outValue->data = cur->unit << Res_value::COMPLEX_UNIT_SHIFT; 1453 // *outScale = cur->scale; 1454 // *outEnd = end; 1455 // //printf("Found unit %s for %s\n", cur->name, str); 1456 // return true; 1457 // } 1458 // cur++; 1459 // } 1460 // 1461 // return false; 1462 //} 1463 // 1464 //bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue) 1465 //{ 1466 // while (len > 0 && isspace16(*s)) { 1467 // s++; 1468 // len--; 1469 // } 1470 // 1471 // if (len <= 0) { 1472 // return false; 1473 // } 1474 // 1475 // size_t i = 0; 1476 // int64_t val = 0; 1477 // bool neg = false; 1478 // 1479 // if (*s == '-') { 1480 // neg = true; 1481 // i++; 1482 // } 1483 // 1484 // if (s[i] < '0' || s[i] > '9') { 1485 // return false; 1486 // } 1487 // 1488 // static_assert(std::is_same<uint32_t, Res_value::data_type>::value, 1489 // "Res_value::data_type has changed. The range checks in this " 1490 // "function are no longer correct."); 1491 // 1492 // // Decimal or hex? 1493 // bool isHex; 1494 // if (len > 1 && s[i] == '0' && s[i+1] == 'x') { 1495 // isHex = true; 1496 // i += 2; 1497 // 1498 // if (neg) { 1499 // return false; 1500 // } 1501 // 1502 // if (i == len) { 1503 // // Just u"0x" 1504 // return false; 1505 // } 1506 // 1507 // bool error = false; 1508 // while (i < len && !error) { 1509 // val = (val*16) + get_hex(s[i], &error); 1510 // i++; 1511 // 1512 // if (val > std::numeric_limits<uint32_t>::max()) { 1513 // return false; 1514 // } 1515 // } 1516 // if (error) { 1517 // return false; 1518 // } 1519 // } else { 1520 // isHex = false; 1521 // while (i < len) { 1522 // if (s[i] < '0' || s[i] > '9') { 1523 // return false; 1524 // } 1525 // val = (val*10) + s[i]-'0'; 1526 // i++; 1527 // 1528 // if ((neg && -val < std::numeric_limits<int32_t>::min()) || 1529 // (!neg && val > std::numeric_limits<int32_t>::max())) { 1530 // return false; 1531 // } 1532 // } 1533 // } 1534 // 1535 // if (neg) val = -val; 1536 // 1537 // while (i < len && isspace16(s[i])) { 1538 // i++; 1539 // } 1540 // 1541 // if (i != len) { 1542 // return false; 1543 // } 1544 // 1545 // if (outValue) { 1546 // outValue->dataType = 1547 // isHex ? outValue->TYPE_INT_HEX : outValue->TYPE_INT_DEC; 1548 // outValue->data = static_cast<Res_value::data_type>(val); 1549 // } 1550 // return true; 1551 //} 1552 // 1553 //bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) 1554 //{ 1555 // return U16StringToInt(s, len, outValue); 1556 //} 1557 // 1558 //bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) 1559 //{ 1560 // while (len > 0 && isspace16(*s)) { 1561 // s++; 1562 // len--; 1563 // } 1564 // 1565 // if (len <= 0) { 1566 // return false; 1567 // } 1568 // 1569 // char buf[128]; 1570 // int i=0; 1571 // while (len > 0 && *s != 0 && i < 126) { 1572 // if (*s > 255) { 1573 // return false; 1574 // } 1575 // buf[i++] = *s++; 1576 // len--; 1577 // } 1578 // 1579 // if (len > 0) { 1580 // return false; 1581 // } 1582 // if ((buf[0] < '0' || buf[0] > '9') && buf[0] != '.' && buf[0] != '-' && buf[0] != '+') { 1583 // return false; 1584 // } 1585 // 1586 // buf[i] = 0; 1587 // const char* end; 1588 // float f = strtof(buf, (char**)&end); 1589 // 1590 // if (*end != 0 && !isspace((unsigned char)*end)) { 1591 // // Might be a unit... 1592 // float scale; 1593 // if (parse_unit(end, outValue, &scale, &end)) { 1594 // f *= scale; 1595 // const bool neg = f < 0; 1596 // if (neg) f = -f; 1597 // uint64_t bits = (uint64_t)(f*(1<<23)+.5f); 1598 // uint32_t radix; 1599 // uint32_t shift; 1600 // if ((bits&0x7fffff) == 0) { 1601 // // Always use 23p0 if there is no fraction, just to make 1602 // // things easier to read. 1603 // radix = Res_value::COMPLEX_RADIX_23p0; 1604 // shift = 23; 1605 // } else if ((bits&0xffffffffff800000LL) == 0) { 1606 // // Magnitude is zero -- can fit in 0 bits of precision. 1607 // radix = Res_value::COMPLEX_RADIX_0p23; 1608 // shift = 0; 1609 // } else if ((bits&0xffffffff80000000LL) == 0) { 1610 // // Magnitude can fit in 8 bits of precision. 1611 // radix = Res_value::COMPLEX_RADIX_8p15; 1612 // shift = 8; 1613 // } else if ((bits&0xffffff8000000000LL) == 0) { 1614 // // Magnitude can fit in 16 bits of precision. 1615 // radix = Res_value::COMPLEX_RADIX_16p7; 1616 // shift = 16; 1617 // } else { 1618 // // Magnitude needs entire range, so no fractional part. 1619 // radix = Res_value::COMPLEX_RADIX_23p0; 1620 // shift = 23; 1621 // } 1622 // int32_t mantissa = (int32_t)( 1623 // (bits>>shift) & Res_value::COMPLEX_MANTISSA_MASK); 1624 // if (neg) { 1625 // mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK; 1626 // } 1627 // outValue->data |= 1628 // (radix<<Res_value::COMPLEX_RADIX_SHIFT) 1629 // | (mantissa<<Res_value::COMPLEX_MANTISSA_SHIFT); 1630 // //printf("Input value: %f 0x%016Lx, mult: %f, radix: %d, shift: %d, final: 0x%08x\n", 1631 // // f * (neg ? -1 : 1), bits, f*(1<<23), 1632 // // radix, shift, outValue->data); 1633 // return true; 1634 // } 1635 // return false; 1636 // } 1637 // 1638 // while (*end != 0 && isspace((unsigned char)*end)) { 1639 // end++; 1640 // } 1641 // 1642 // if (*end == 0) { 1643 // if (outValue) { 1644 // outValue->dataType = outValue->TYPE_FLOAT; 1645 // *(float*)(&outValue->data) = f; 1646 // return true; 1647 // } 1648 // } 1649 // 1650 // return false; 1651 //} 1652 // 1653 //bool ResTable::stringToValue(Res_value* outValue, String16* outString, 1654 // const char16_t* s, size_t len, 1655 // bool preserveSpaces, bool coerceType, 1656 // uint32_t attrID, 1657 // const String16* defType, 1658 // const String16* defPackage, 1659 // Accessor* accessor, 1660 // void* accessorCookie, 1661 // uint32_t attrType, 1662 // bool enforcePrivate) const 1663 //{ 1664 // bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting(); 1665 // const char* errorMsg = NULL; 1666 // 1667 // outValue->size = sizeof(Res_value); 1668 // outValue->res0 = 0; 1669 // 1670 // // First strip leading/trailing whitespace. Do this before handling 1671 // // escapes, so they can be used to force whitespace into the string. 1672 // if (!preserveSpaces) { 1673 // while (len > 0 && isspace16(*s)) { 1674 // s++; 1675 // len--; 1676 // } 1677 // while (len > 0 && isspace16(s[len-1])) { 1678 // len--; 1679 // } 1680 // // If the string ends with '\', then we keep the space after it. 1681 // if (len > 0 && s[len-1] == '\\' && s[len] != 0) { 1682 // len++; 1683 // } 1684 // } 1685 // 1686 // //printf("Value for: %s\n", String8(s, len).string()); 1687 // 1688 // uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED; 1689 // uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff; 1690 // bool fromAccessor = false; 1691 // if (attrID != 0 && !Res_INTERNALID(attrID)) { 1692 // const ssize_t p = getResourcePackageIndex(attrID); 1693 // const bag_entry* bag; 1694 // ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; 1695 // //printf("For attr 0x%08x got bag of %d\n", attrID, cnt); 1696 // if (cnt >= 0) { 1697 // while (cnt > 0) { 1698 // //printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data); 1699 // switch (bag->map.name.ident) { 1700 // case ResTable_map::ATTR_TYPE: 1701 // attrType = bag->map.value.data; 1702 // break; 1703 // case ResTable_map::ATTR_MIN: 1704 // attrMin = bag->map.value.data; 1705 // break; 1706 // case ResTable_map::ATTR_MAX: 1707 // attrMax = bag->map.value.data; 1708 // break; 1709 // case ResTable_map::ATTR_L10N: 1710 // l10nReq = bag->map.value.data; 1711 // break; 1712 // } 1713 // bag++; 1714 // cnt--; 1715 // } 1716 // unlockBag(bag); 1717 // } else if (accessor && accessor->getAttributeType(attrID, &attrType)) { 1718 // fromAccessor = true; 1719 // if (attrType == ResTable_map::TYPE_ENUM 1720 // || attrType == ResTable_map::TYPE_FLAGS 1721 // || attrType == ResTable_map::TYPE_INTEGER) { 1722 // accessor->getAttributeMin(attrID, &attrMin); 1723 // accessor->getAttributeMax(attrID, &attrMax); 1724 // } 1725 // if (localizationSetting) { 1726 // l10nReq = accessor->getAttributeL10N(attrID); 1727 // } 1728 // } 1729 // } 1730 // 1731 // const bool canStringCoerce = 1732 // coerceType && (attrType&ResTable_map::TYPE_STRING) != 0; 1733 // 1734 // if (*s == '@') { 1735 // outValue->dataType = outValue->TYPE_REFERENCE; 1736 // 1737 // // Note: we don't check attrType here because the reference can 1738 // // be to any other type; we just need to count on the client making 1739 // // sure the referenced type is correct. 1740 // 1741 // //printf("Looking up ref: %s\n", String8(s, len).string()); 1742 // 1743 // // It's a reference! 1744 // if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') { 1745 // // Special case @null as undefined. This will be converted by 1746 // // AssetManager to TYPE_NULL with data DATA_NULL_UNDEFINED. 1747 // outValue->data = 0; 1748 // return true; 1749 // } else if (len == 6 && s[1]=='e' && s[2]=='m' && s[3]=='p' && s[4]=='t' && s[5]=='y') { 1750 // // Special case @empty as explicitly defined empty value. 1751 // outValue->dataType = Res_value::TYPE_NULL; 1752 // outValue->data = Res_value::DATA_NULL_EMPTY; 1753 // return true; 1754 // } else { 1755 // bool createIfNotFound = false; 1756 // const char16_t* resourceRefName; 1757 // int resourceNameLen; 1758 // if (len > 2 && s[1] == '+') { 1759 // createIfNotFound = true; 1760 // resourceRefName = s + 2; 1761 // resourceNameLen = len - 2; 1762 // } else if (len > 2 && s[1] == '*') { 1763 // enforcePrivate = false; 1764 // resourceRefName = s + 2; 1765 // resourceNameLen = len - 2; 1766 // } else { 1767 // createIfNotFound = false; 1768 // resourceRefName = s + 1; 1769 // resourceNameLen = len - 1; 1770 // } 1771 // String16 package, type, name; 1772 // if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name, 1773 // defType, defPackage, &errorMsg)) { 1774 // if (accessor != NULL) { 1775 // accessor->reportError(accessorCookie, errorMsg); 1776 // } 1777 // return false; 1778 // } 1779 // 1780 // uint32_t specFlags = 0; 1781 // uint32_t rid = identifierForName(name.string(), name.size(), type.string(), 1782 // type.size(), package.string(), package.size(), &specFlags); 1783 // if (rid != 0) { 1784 // if (enforcePrivate) { 1785 // if (accessor == NULL || accessor->getAssetsPackage() != package) { 1786 // if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { 1787 // if (accessor != NULL) { 1788 // accessor->reportError(accessorCookie, "Resource is not public."); 1789 // } 1790 // return false; 1791 // } 1792 // } 1793 // } 1794 // 1795 // if (accessor) { 1796 // rid = Res_MAKEID( 1797 // accessor->getRemappedPackage(Res_GETPACKAGE(rid)), 1798 // Res_GETTYPE(rid), Res_GETENTRY(rid)); 1799 // if (kDebugTableNoisy) { 1800 // ALOGI("Incl %s:%s/%s: 0x%08x\n", 1801 // String8(package).string(), String8(type).string(), 1802 // String8(name).string(), rid); 1803 // } 1804 // } 1805 // 1806 // uint32_t packageId = Res_GETPACKAGE(rid) + 1; 1807 // if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) { 1808 // outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE; 1809 // } 1810 // outValue->data = rid; 1811 // return true; 1812 // } 1813 // 1814 // if (accessor) { 1815 // uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name, 1816 // createIfNotFound); 1817 // if (rid != 0) { 1818 // if (kDebugTableNoisy) { 1819 // ALOGI("Pckg %s:%s/%s: 0x%08x\n", 1820 // String8(package).string(), String8(type).string(), 1821 // String8(name).string(), rid); 1822 // } 1823 // uint32_t packageId = Res_GETPACKAGE(rid) + 1; 1824 // if (packageId == 0x00) { 1825 // outValue->data = rid; 1826 // outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE; 1827 // return true; 1828 // } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) { 1829 // // We accept packageId's generated as 0x01 in order to support 1830 // // building the android system resources 1831 // outValue->data = rid; 1832 // return true; 1833 // } 1834 // } 1835 // } 1836 // } 1837 // 1838 // if (accessor != NULL) { 1839 // accessor->reportError(accessorCookie, "No resource found that matches the given name"); 1840 // } 1841 // return false; 1842 // } 1843 // 1844 // // if we got to here, and localization is required and it's not a reference, 1845 // // complain and bail. 1846 // if (l10nReq == ResTable_map::L10N_SUGGESTED) { 1847 // if (localizationSetting) { 1848 // if (accessor != NULL) { 1849 // accessor->reportError(accessorCookie, "This attribute must be localized."); 1850 // } 1851 // } 1852 // } 1853 // 1854 // if (*s == '#') { 1855 // // It's a color! Convert to an integer of the form 0xaarrggbb. 1856 // uint32_t color = 0; 1857 // bool error = false; 1858 // if (len == 4) { 1859 // outValue->dataType = outValue->TYPE_INT_COLOR_RGB4; 1860 // color |= 0xFF000000; 1861 // color |= get_hex(s[1], &error) << 20; 1862 // color |= get_hex(s[1], &error) << 16; 1863 // color |= get_hex(s[2], &error) << 12; 1864 // color |= get_hex(s[2], &error) << 8; 1865 // color |= get_hex(s[3], &error) << 4; 1866 // color |= get_hex(s[3], &error); 1867 // } else if (len == 5) { 1868 // outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4; 1869 // color |= get_hex(s[1], &error) << 28; 1870 // color |= get_hex(s[1], &error) << 24; 1871 // color |= get_hex(s[2], &error) << 20; 1872 // color |= get_hex(s[2], &error) << 16; 1873 // color |= get_hex(s[3], &error) << 12; 1874 // color |= get_hex(s[3], &error) << 8; 1875 // color |= get_hex(s[4], &error) << 4; 1876 // color |= get_hex(s[4], &error); 1877 // } else if (len == 7) { 1878 // outValue->dataType = outValue->TYPE_INT_COLOR_RGB8; 1879 // color |= 0xFF000000; 1880 // color |= get_hex(s[1], &error) << 20; 1881 // color |= get_hex(s[2], &error) << 16; 1882 // color |= get_hex(s[3], &error) << 12; 1883 // color |= get_hex(s[4], &error) << 8; 1884 // color |= get_hex(s[5], &error) << 4; 1885 // color |= get_hex(s[6], &error); 1886 // } else if (len == 9) { 1887 // outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8; 1888 // color |= get_hex(s[1], &error) << 28; 1889 // color |= get_hex(s[2], &error) << 24; 1890 // color |= get_hex(s[3], &error) << 20; 1891 // color |= get_hex(s[4], &error) << 16; 1892 // color |= get_hex(s[5], &error) << 12; 1893 // color |= get_hex(s[6], &error) << 8; 1894 // color |= get_hex(s[7], &error) << 4; 1895 // color |= get_hex(s[8], &error); 1896 // } else { 1897 // error = true; 1898 // } 1899 // if (!error) { 1900 // if ((attrType&ResTable_map::TYPE_COLOR) == 0) { 1901 // if (!canStringCoerce) { 1902 // if (accessor != NULL) { 1903 // accessor->reportError(accessorCookie, 1904 // "Color types not allowed"); 1905 // } 1906 // return false; 1907 // } 1908 // } else { 1909 // outValue->data = color; 1910 // //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color); 1911 // return true; 1912 // } 1913 // } else { 1914 // if ((attrType&ResTable_map::TYPE_COLOR) != 0) { 1915 // if (accessor != NULL) { 1916 // accessor->reportError(accessorCookie, "Color value not valid --" 1917 // " must be #rgb, #argb, #rrggbb, or #aarrggbb"); 1918 // } 1919 // #if 0 1920 // fprintf(stderr, "%s: Color ID %s value %s is not valid\n", 1921 // "Resource File", //(const char*)in->getPrintableSource(), 1922 // String8(*curTag).string(), 1923 // String8(s, len).string()); 1924 // #endif 1925 // return false; 1926 // } 1927 // } 1928 // } 1929 // 1930 // if (*s == '?') { 1931 // outValue->dataType = outValue->TYPE_ATTRIBUTE; 1932 // 1933 // // Note: we don't check attrType here because the reference can 1934 // // be to any other type; we just need to count on the client making 1935 // // sure the referenced type is correct. 1936 // 1937 // //printf("Looking up attr: %s\n", String8(s, len).string()); 1938 // 1939 // static const String16 attr16("attr"); 1940 // String16 package, type, name; 1941 // if (!expandResourceRef(s+1, len-1, &package, &type, &name, 1942 // &attr16, defPackage, &errorMsg)) { 1943 // if (accessor != NULL) { 1944 // accessor->reportError(accessorCookie, errorMsg); 1945 // } 1946 // return false; 1947 // } 1948 // 1949 // //printf("Pkg: %s, Type: %s, Name: %s\n", 1950 // // String8(package).string(), String8(type).string(), 1951 // // String8(name).string()); 1952 // uint32_t specFlags = 0; 1953 // uint32_t rid = 1954 // identifierForName(name.string(), name.size(), 1955 // type.string(), type.size(), 1956 // package.string(), package.size(), &specFlags); 1957 // if (rid != 0) { 1958 // if (enforcePrivate) { 1959 // if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { 1960 // if (accessor != NULL) { 1961 // accessor->reportError(accessorCookie, "Attribute is not public."); 1962 // } 1963 // return false; 1964 // } 1965 // } 1966 // 1967 // if (accessor) { 1968 // rid = Res_MAKEID( 1969 // accessor->getRemappedPackage(Res_GETPACKAGE(rid)), 1970 // Res_GETTYPE(rid), Res_GETENTRY(rid)); 1971 // } 1972 // 1973 // uint32_t packageId = Res_GETPACKAGE(rid) + 1; 1974 // if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID) { 1975 // outValue->dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE; 1976 // } 1977 // outValue->data = rid; 1978 // return true; 1979 // } 1980 // 1981 // if (accessor) { 1982 // uint32_t rid = accessor->getCustomResource(package, type, name); 1983 // if (rid != 0) { 1984 // uint32_t packageId = Res_GETPACKAGE(rid) + 1; 1985 // if (packageId == 0x00) { 1986 // outValue->data = rid; 1987 // outValue->dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE; 1988 // return true; 1989 // } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) { 1990 // // We accept packageId's generated as 0x01 in order to support 1991 // // building the android system resources 1992 // outValue->data = rid; 1993 // return true; 1994 // } 1995 // } 1996 // } 1997 // 1998 // if (accessor != NULL) { 1999 // accessor->reportError(accessorCookie, "No resource found that matches the given name"); 2000 // } 2001 // return false; 2002 // } 2003 // 2004 // if (stringToInt(s, len, outValue)) { 2005 // if ((attrType&ResTable_map::TYPE_INTEGER) == 0) { 2006 // // If this type does not allow integers, but does allow floats, 2007 // // fall through on this error case because the float type should 2008 // // be able to accept any integer value. 2009 // if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) { 2010 // if (accessor != NULL) { 2011 // accessor->reportError(accessorCookie, "Integer types not allowed"); 2012 // } 2013 // return false; 2014 // } 2015 // } else { 2016 // if (((int32_t)outValue->data) < ((int32_t)attrMin) 2017 // || ((int32_t)outValue->data) > ((int32_t)attrMax)) { 2018 // if (accessor != NULL) { 2019 // accessor->reportError(accessorCookie, "Integer value out of range"); 2020 // } 2021 // return false; 2022 // } 2023 // return true; 2024 // } 2025 // } 2026 // 2027 // if (stringToFloat(s, len, outValue)) { 2028 // if (outValue->dataType == Res_value::TYPE_DIMENSION) { 2029 // if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) { 2030 // return true; 2031 // } 2032 // if (!canStringCoerce) { 2033 // if (accessor != NULL) { 2034 // accessor->reportError(accessorCookie, "Dimension types not allowed"); 2035 // } 2036 // return false; 2037 // } 2038 // } else if (outValue->dataType == Res_value::TYPE_FRACTION) { 2039 // if ((attrType&ResTable_map::TYPE_FRACTION) != 0) { 2040 // return true; 2041 // } 2042 // if (!canStringCoerce) { 2043 // if (accessor != NULL) { 2044 // accessor->reportError(accessorCookie, "Fraction types not allowed"); 2045 // } 2046 // return false; 2047 // } 2048 // } else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) { 2049 // if (!canStringCoerce) { 2050 // if (accessor != NULL) { 2051 // accessor->reportError(accessorCookie, "Float types not allowed"); 2052 // } 2053 // return false; 2054 // } 2055 // } else { 2056 // return true; 2057 // } 2058 // } 2059 // 2060 // if (len == 4) { 2061 // if ((s[0] == 't' || s[0] == 'T') && 2062 // (s[1] == 'r' || s[1] == 'R') && 2063 // (s[2] == 'u' || s[2] == 'U') && 2064 // (s[3] == 'e' || s[3] == 'E')) { 2065 // if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { 2066 // if (!canStringCoerce) { 2067 // if (accessor != NULL) { 2068 // accessor->reportError(accessorCookie, "Boolean types not allowed"); 2069 // } 2070 // return false; 2071 // } 2072 // } else { 2073 // outValue->dataType = outValue->TYPE_INT_BOOLEAN; 2074 // outValue->data = (uint32_t)-1; 2075 // return true; 2076 // } 2077 // } 2078 // } 2079 // 2080 // if (len == 5) { 2081 // if ((s[0] == 'f' || s[0] == 'F') && 2082 // (s[1] == 'a' || s[1] == 'A') && 2083 // (s[2] == 'l' || s[2] == 'L') && 2084 // (s[3] == 's' || s[3] == 'S') && 2085 // (s[4] == 'e' || s[4] == 'E')) { 2086 // if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { 2087 // if (!canStringCoerce) { 2088 // if (accessor != NULL) { 2089 // accessor->reportError(accessorCookie, "Boolean types not allowed"); 2090 // } 2091 // return false; 2092 // } 2093 // } else { 2094 // outValue->dataType = outValue->TYPE_INT_BOOLEAN; 2095 // outValue->data = 0; 2096 // return true; 2097 // } 2098 // } 2099 // } 2100 // 2101 // if ((attrType&ResTable_map::TYPE_ENUM) != 0) { 2102 // const ssize_t p = getResourcePackageIndex(attrID); 2103 // const bag_entry* bag; 2104 // ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; 2105 // //printf("Got %d for enum\n", cnt); 2106 // if (cnt >= 0) { 2107 // resource_name rname; 2108 // while (cnt > 0) { 2109 // if (!Res_INTERNALID(bag->map.name.ident)) { 2110 // //printf("Trying attr #%08x\n", bag->map.name.ident); 2111 // if (getResourceName(bag->map.name.ident, false, &rname)) { 2112 // #if 0 2113 // printf("Matching %s against %s (0x%08x)\n", 2114 // String8(s, len).string(), 2115 // String8(rname.name, rname.nameLen).string(), 2116 // bag->map.name.ident); 2117 // #endif 2118 // if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) { 2119 // outValue->dataType = bag->map.value.dataType; 2120 // outValue->data = bag->map.value.data; 2121 // unlockBag(bag); 2122 // return true; 2123 // } 2124 // } 2125 // 2126 // } 2127 // bag++; 2128 // cnt--; 2129 // } 2130 // unlockBag(bag); 2131 // } 2132 // 2133 // if (fromAccessor) { 2134 // if (accessor->getAttributeEnum(attrID, s, len, outValue)) { 2135 // return true; 2136 // } 2137 // } 2138 // } 2139 // 2140 // if ((attrType&ResTable_map::TYPE_FLAGS) != 0) { 2141 // const ssize_t p = getResourcePackageIndex(attrID); 2142 // const bag_entry* bag; 2143 // ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; 2144 // //printf("Got %d for flags\n", cnt); 2145 // if (cnt >= 0) { 2146 // bool failed = false; 2147 // resource_name rname; 2148 // outValue->dataType = Res_value::TYPE_INT_HEX; 2149 // outValue->data = 0; 2150 // const char16_t* end = s + len; 2151 // const char16_t* pos = s; 2152 // while (pos < end && !failed) { 2153 // const char16_t* start = pos; 2154 // pos++; 2155 // while (pos < end && *pos != '|') { 2156 // pos++; 2157 // } 2158 // //printf("Looking for: %s\n", String8(start, pos-start).string()); 2159 // const bag_entry* bagi = bag; 2160 // ssize_t i; 2161 // for (i=0; i<cnt; i++, bagi++) { 2162 // if (!Res_INTERNALID(bagi->map.name.ident)) { 2163 // //printf("Trying attr #%08x\n", bagi->map.name.ident); 2164 // if (getResourceName(bagi->map.name.ident, false, &rname)) { 2165 // #if 0 2166 // printf("Matching %s against %s (0x%08x)\n", 2167 // String8(start,pos-start).string(), 2168 // String8(rname.name, rname.nameLen).string(), 2169 // bagi->map.name.ident); 2170 // #endif 2171 // if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) { 2172 // outValue->data |= bagi->map.value.data; 2173 // break; 2174 // } 2175 // } 2176 // } 2177 // } 2178 // if (i >= cnt) { 2179 // // Didn't find this flag identifier. 2180 // failed = true; 2181 // } 2182 // if (pos < end) { 2183 // pos++; 2184 // } 2185 // } 2186 // unlockBag(bag); 2187 // if (!failed) { 2188 // //printf("Final flag value: 0x%lx\n", outValue->data); 2189 // return true; 2190 // } 2191 // } 2192 // 2193 // 2194 // if (fromAccessor) { 2195 // if (accessor->getAttributeFlags(attrID, s, len, outValue)) { 2196 // //printf("Final flag value: 0x%lx\n", outValue->data); 2197 // return true; 2198 // } 2199 // } 2200 // } 2201 // 2202 // if ((attrType&ResTable_map::TYPE_STRING) == 0) { 2203 // if (accessor != NULL) { 2204 // accessor->reportError(accessorCookie, "String types not allowed"); 2205 // } 2206 // return false; 2207 // } 2208 // 2209 // // Generic string handling... 2210 // outValue->dataType = outValue->TYPE_STRING; 2211 // if (outString) { 2212 // bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg); 2213 // if (accessor != NULL) { 2214 // accessor->reportError(accessorCookie, errorMsg); 2215 // } 2216 // return failed; 2217 // } 2218 // 2219 // return true; 2220 //} 2221 // 2222 //bool ResTable::collectString(String16* outString, 2223 // const char16_t* s, size_t len, 2224 // bool preserveSpaces, 2225 // const char** outErrorMsg, 2226 // bool append) 2227 //{ 2228 // String16 tmp; 2229 // 2230 // char quoted = 0; 2231 // const char16_t* p = s; 2232 // while (p < (s+len)) { 2233 // while (p < (s+len)) { 2234 // const char16_t c = *p; 2235 // if (c == '\\') { 2236 // break; 2237 // } 2238 // if (!preserveSpaces) { 2239 // if (quoted == 0 && isspace16(c) 2240 // && (c != ' ' || isspace16(*(p+1)))) { 2241 // break; 2242 // } 2243 // if (c == '"' && (quoted == 0 || quoted == '"')) { 2244 // break; 2245 // } 2246 // if (c == '\'' && (quoted == 0 || quoted == '\'')) { 2247 // /* 2248 // * In practice, when people write ' instead of \' 2249 // * in a string, they are doing it by accident 2250 // * instead of really meaning to use ' as a quoting 2251 // * character. Warn them so they don't lose it. 2252 // */ 2253 // if (outErrorMsg) { 2254 // *outErrorMsg = "Apostrophe not preceded by \\"; 2255 // } 2256 // return false; 2257 // } 2258 // } 2259 // p++; 2260 // } 2261 // if (p < (s+len)) { 2262 // if (p > s) { 2263 // tmp.append(String16(s, p-s)); 2264 // } 2265 // if (!preserveSpaces && (*p == '"' || *p == '\'')) { 2266 // if (quoted == 0) { 2267 // quoted = *p; 2268 // } else { 2269 // quoted = 0; 2270 // } 2271 // p++; 2272 // } else if (!preserveSpaces && isspace16(*p)) { 2273 // // Space outside of a quote -- consume all spaces and 2274 // // leave a single plain space char. 2275 // tmp.append(String16(" ")); 2276 // p++; 2277 // while (p < (s+len) && isspace16(*p)) { 2278 // p++; 2279 // } 2280 // } else if (*p == '\\') { 2281 // p++; 2282 // if (p < (s+len)) { 2283 // switch (*p) { 2284 // case 't': 2285 // tmp.append(String16("\t")); 2286 // break; 2287 // case 'n': 2288 // tmp.append(String16("\n")); 2289 // break; 2290 // case '#': 2291 // tmp.append(String16("#")); 2292 // break; 2293 // case '@': 2294 // tmp.append(String16("@")); 2295 // break; 2296 // case '?': 2297 // tmp.append(String16("?")); 2298 // break; 2299 // case '"': 2300 // tmp.append(String16("\"")); 2301 // break; 2302 // case '\'': 2303 // tmp.append(String16("'")); 2304 // break; 2305 // case '\\': 2306 // tmp.append(String16("\\")); 2307 // break; 2308 // case 'u': 2309 // { 2310 // char16_t chr = 0; 2311 // int i = 0; 2312 // while (i < 4 && p[1] != 0) { 2313 // p++; 2314 // i++; 2315 // int c; 2316 // if (*p >= '0' && *p <= '9') { 2317 // c = *p - '0'; 2318 // } else if (*p >= 'a' && *p <= 'f') { 2319 // c = *p - 'a' + 10; 2320 // } else if (*p >= 'A' && *p <= 'F') { 2321 // c = *p - 'A' + 10; 2322 // } else { 2323 // if (outErrorMsg) { 2324 // *outErrorMsg = "Bad character in \\u unicode escape sequence"; 2325 // } 2326 // return false; 2327 // } 2328 // chr = (chr<<4) | c; 2329 // } 2330 // tmp.append(String16(&chr, 1)); 2331 // } break; 2332 // default: 2333 // // ignore unknown escape chars. 2334 // break; 2335 // } 2336 // p++; 2337 // } 2338 // } 2339 // len -= (p-s); 2340 // s = p; 2341 // } 2342 // } 2343 // 2344 // if (tmp.size() != 0) { 2345 // if (len > 0) { 2346 // tmp.append(String16(s, len)); 2347 // } 2348 // if (append) { 2349 // outString->append(tmp); 2350 // } else { 2351 // outString->setTo(tmp); 2352 // } 2353 // } else { 2354 // if (append) { 2355 // outString->append(String16(s, len)); 2356 // } else { 2357 // outString->setTo(s, len); 2358 // } 2359 // } 2360 // 2361 // return true; 2362 //} 2363 2364 public int getBasePackageCount() 2365 { 2366 if (mError != NO_ERROR) { 2367 return 0; 2368 } 2369 return mPackageGroups.size(); 2370 } 2371 2372 public String getBasePackageName(int idx) 2373 { 2374 if (mError != NO_ERROR) { 2375 return null; 2376 } 2377 LOG_FATAL_IF(idx >= mPackageGroups.size(), 2378 "Requested package index %d past package count %d", 2379 (int)idx, (int)mPackageGroups.size()); 2380 return mPackageGroups.get(keyFor(idx)).name; 2381 } 2382 2383 public int getBasePackageId(int idx) 2384 { 2385 if (mError != NO_ERROR) { 2386 return 0; 2387 } 2388 LOG_FATAL_IF(idx >= mPackageGroups.size(), 2389 "Requested package index %d past package count %d", 2390 (int)idx, (int)mPackageGroups.size()); 2391 return mPackageGroups.get(keyFor(idx)).id; 2392 } 2393 getLastTypeIdForPackage(int idx)2394 int getLastTypeIdForPackage(int idx) 2395 { 2396 if (mError != NO_ERROR) { 2397 return 0; 2398 } 2399 LOG_FATAL_IF(idx >= mPackageGroups.size(), 2400 "Requested package index %d past package count %d", 2401 (int)idx, (int)mPackageGroups.size()); 2402 PackageGroup group = mPackageGroups.get(keyFor(idx)); 2403 return group.largestTypeId; 2404 } 2405 keyFor(int idx)2406 int keyFor(int idx) { 2407 ArrayList<Integer> keys = new ArrayList<>(mPackageGroups.keySet()); 2408 Collections.sort(keys); 2409 return keys.get(idx); 2410 } 2411 getTableCount()2412 public int getTableCount() { 2413 return mHeaders.size(); 2414 } 2415 getTableStringBlock(int index)2416 public ResStringPool getTableStringBlock(int index) { 2417 return mHeaders.get(index).values; 2418 } 2419 getDynamicRefTableForCookie(int cookie)2420 public DynamicRefTable getDynamicRefTableForCookie(int cookie) { 2421 for (PackageGroup pg : mPackageGroups.values()) { 2422 int M = pg.packages.size(); 2423 for (int j = 0; j < M; j++) { 2424 if (pg.packages.get(j).header.cookie == cookie) { 2425 return pg.dynamicRefTable; 2426 } 2427 } 2428 } 2429 return null; 2430 } 2431 getResourceName(int resID, boolean allowUtf8, ResourceName outName)2432 public boolean getResourceName(int resID, boolean allowUtf8, ResourceName outName) { 2433 if (mError != NO_ERROR) { 2434 return false; 2435 } 2436 2437 final int p = getResourcePackageIndex(resID); 2438 final int t = Res_GETTYPE(resID); 2439 final int e = Res_GETENTRY(resID); 2440 2441 if (p < 0) { 2442 if (Res_GETPACKAGE(resID)+1 == 0) { 2443 ALOGW("No package identifier when getting name for resource number 0x%08x", resID); 2444 } 2445 return false; 2446 } 2447 if (t < 0) { 2448 ALOGW("No type identifier when getting name for resource number 0x%08x", resID); 2449 return false; 2450 } 2451 2452 final PackageGroup grp = mPackageGroups.get(p); 2453 if (grp == NULL) { 2454 ALOGW("Bad identifier when getting name for resource number 0x%08x", resID); 2455 return false; 2456 } 2457 2458 Entry entry = new Entry(); 2459 int err = getEntry(grp, t, e, null, entry); 2460 if (err != NO_ERROR) { 2461 return false; 2462 } 2463 2464 outName.packageName = grp.name; 2465 outName.type = entry.typeStr.string(); 2466 if (outName.type == null) { 2467 return false; 2468 } 2469 outName.name = entry.keyStr.string(); 2470 if (outName.name == null) { 2471 return false; 2472 } 2473 2474 return true; 2475 } 2476 getResourceName(int resId)2477 String getResourceName(int resId) { 2478 ResourceName outName = new ResourceName(); 2479 if (getResourceName(resId, true, outName)) { 2480 return outName.toString(); 2481 } 2482 throw new IllegalArgumentException("Unknown resource id " + resId); 2483 } 2484 2485 // A group of objects describing a particular resource package. 2486 // The first in 'package' is always the root object (from the resource 2487 // table that defined the package); the ones after are skins on top of it. 2488 // from ResourceTypes.cpp struct ResTable::PackageGroup 2489 public static class PackageGroup 2490 { PackageGroup( ResTable _owner, final String _name, int _id, boolean appAsLib, boolean _isSystemAsset, boolean _isDynamic)2491 public PackageGroup( 2492 ResTable _owner, final String _name, int _id, 2493 boolean appAsLib, boolean _isSystemAsset, boolean _isDynamic) 2494 // : owner(_owner) 2495 // , name(_name) 2496 // , id(_id) 2497 // , largestTypeId(0) 2498 // , dynamicRefTable(static_cast<uint8_t>(_id), appAsLib) 2499 // , isSystemAsset(_isSystemAsset) 2500 { 2501 this.owner = _owner; 2502 this.name = _name; 2503 this.id = _id; 2504 this.dynamicRefTable = new DynamicRefTable((byte) _id, appAsLib); 2505 this.isSystemAsset = _isSystemAsset; 2506 this.isDynamic = _isDynamic; 2507 } 2508 2509 // ~PackageGroup() { 2510 // clearBagCache(); 2511 // final int numTypes = types.size(); 2512 // for (int i = 0; i < numTypes; i++) { 2513 // final List<DataType> typeList = types.get(i); 2514 // final int numInnerTypes = typeList.size(); 2515 // for (int j = 0; j < numInnerTypes; j++) { 2516 // if (typeList.get(j)._package_.owner == owner) { 2517 // delete typeList[j]; 2518 // } 2519 // } 2520 // typeList.clear(); 2521 // } 2522 // 2523 // final int N = packages.size(); 2524 // for (int i=0; i<N; i++) { 2525 // ResTable_package pkg = packages[i]; 2526 // if (pkg.owner == owner) { 2527 // delete pkg; 2528 // } 2529 // } 2530 // } 2531 2532 /** 2533 * Clear all cache related data that depends on parameters/configuration. 2534 * This includes the bag caches and filtered types. 2535 */ clearBagCache()2536 void clearBagCache() { 2537 // for (int i = 0; i < typeCacheEntries.size(); i++) { 2538 // if (kDebugTableNoisy) { 2539 // printf("type=0x%x\n", i); 2540 // } 2541 // final List<DataType> typeList = types.get(i); 2542 // if (!typeList.isEmpty()) { 2543 // TypeCacheEntry cacheEntry = typeCacheEntries.editItemAt(i); 2544 // 2545 // // Reset the filtered configurations. 2546 // cacheEntry.filteredConfigs.clear(); 2547 // 2548 // bag_set[][] typeBags = cacheEntry.cachedBags; 2549 // if (kDebugTableNoisy) { 2550 // printf("typeBags=%s\n", typeBags); 2551 // } 2552 // 2553 // if (isTruthy(typeBags)) { 2554 // final int N = typeList.get(0).entryCount; 2555 // if (kDebugTableNoisy) { 2556 // printf("type.entryCount=0x%x\n", N); 2557 // } 2558 // for (int j = 0; j < N; j++) { 2559 // if (typeBags[j] && typeBags[j] != (bag_set *) 0xFFFFFFFF){ 2560 // free(typeBags[j]); 2561 // } 2562 // } 2563 // free(typeBags); 2564 // cacheEntry.cachedBags = NULL; 2565 // } 2566 // } 2567 // } 2568 } 2569 2570 // long findType16(final String type, int len) { 2571 // final int N = packages.size(); 2572 // for (int i = 0; i < N; i++) { 2573 // sint index = packages[i].typeStrings.indexOfString(type, len); 2574 // if (index >= 0) { 2575 // return index + packages[i].typeIdOffset; 2576 // } 2577 // } 2578 // return -1; 2579 // } 2580 2581 final ResTable owner; 2582 final String name; 2583 final int id; 2584 2585 // This is mainly used to keep track of the loaded packages 2586 // and to clean them up properly. Accessing resources happens from 2587 // the 'types' array. 2588 List<ResTablePackage> packages = new ArrayList<>(); 2589 2590 public final Map<Integer, List<Type>> types = new HashMap<>(); 2591 2592 byte largestTypeId; 2593 2594 // Cached objects dependent on the parameters/configuration of this ResTable. 2595 // Gets cleared whenever the parameters/configuration changes. 2596 // These are stored here in a parallel structure because the data in `types` may 2597 // be shared by other ResTable's (framework resources are shared this way). 2598 ByteBucketArray<TypeCacheEntry> typeCacheEntries = 2599 new ByteBucketArray<TypeCacheEntry>(new TypeCacheEntry()) { 2600 @Override 2601 TypeCacheEntry newInstance() { 2602 return new TypeCacheEntry(); 2603 } 2604 }; 2605 2606 // The table mapping dynamic references to resolved references for 2607 // this package group. 2608 // TODO: We may be able to support dynamic references in overlays 2609 // by having these tables in a per-package scope rather than 2610 // per-package-group. 2611 DynamicRefTable dynamicRefTable; 2612 2613 // If the package group comes from a system asset. Used in 2614 // determining non-system locales. 2615 final boolean isSystemAsset; 2616 final boolean isDynamic; 2617 } 2618 2619 // -------------------------------------------------------------------- 2620 // -------------------------------------------------------------------- 2621 // -------------------------------------------------------------------- 2622 2623 // struct ResTable::Header 2624 public static class Header 2625 { 2626 // Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL), 2627 // resourceIDMap(NULL), resourceIDMapSize(0) { } 2628 Header(ResTable owner)2629 public Header(ResTable owner) { 2630 this.owner = owner; 2631 } 2632 2633 // ~Header() 2634 // { 2635 // free(resourceIDMap); 2636 // } 2637 2638 ResTable owner; 2639 byte[] ownedData; 2640 ResTable_header header; 2641 int size; 2642 int dataEnd; 2643 int index; 2644 int cookie; 2645 2646 ResStringPool values = new ResStringPool(); 2647 int[] resourceIDMap; 2648 int resourceIDMapSize; 2649 }; 2650 2651 public static class Entry { 2652 ResTable_config config; 2653 ResTable_entry entry; 2654 ResTable_type type; 2655 int specFlags; 2656 ResTablePackage _package_; 2657 2658 StringPoolRef typeStr; 2659 StringPoolRef keyStr; 2660 } 2661 2662 // struct ResTable::DataType 2663 public static class Type { 2664 2665 final Header header; 2666 final ResTablePackage _package_; 2667 public final int entryCount; 2668 public ResTable_typeSpec typeSpec; 2669 public int[] typeSpecFlags; 2670 public IdmapEntries idmapEntries = new IdmapEntries(); 2671 public List<ResTable_type> configs; 2672 Type(final Header _header, final ResTablePackage _package, int count)2673 public Type(final Header _header, final ResTablePackage _package, int count) 2674 // : header(_header), package(_package), entryCount(count), 2675 // typeSpec(NULL), typeSpecFlags(NULL) { } 2676 { 2677 this.header = _header; 2678 _package_ = _package; 2679 this.entryCount = count; 2680 this.typeSpec = null; 2681 this.typeSpecFlags = null; 2682 this.configs = new ArrayList<>(); 2683 } 2684 } 2685 2686 // struct ResTable::Package 2687 public static class ResTablePackage { 2688 // Package(ResTable* _owner, final Header* _header, final ResTable_package* _package) 2689 // : owner(_owner), header(_header), package(_package), typeIdOffset(0) { 2690 // if (dtohs(package.header.headerSize) == sizeof(package)) { 2691 // // The package structure is the same size as the definition. 2692 // // This means it contains the typeIdOffset field. 2693 // typeIdOffset = package.typeIdOffset; 2694 // } 2695 ResTablePackage(ResTable owner, Header header, ResTable_package _package)2696 public ResTablePackage(ResTable owner, Header header, ResTable_package _package) { 2697 this.owner = owner; 2698 this.header = header; 2699 this._package_ = _package; 2700 } 2701 2702 final ResTable owner; 2703 final Header header; 2704 final ResTable_package _package_; 2705 2706 ResStringPool typeStrings = new ResStringPool(); 2707 ResStringPool keyStrings = new ResStringPool(); 2708 2709 int typeIdOffset; 2710 }; 2711 2712 public static class bag_entry { 2713 public int stringBlock; 2714 public ResTable_map map = new ResTable_map(); 2715 } 2716 lock()2717 public void lock() { 2718 mLock.acquireUninterruptibly(); 2719 } 2720 unlock()2721 public void unlock() { 2722 mLock.release(); 2723 } 2724 lockBag(int resID, Ref<bag_entry[]> outBag)2725 public int lockBag(int resID, Ref<bag_entry[]> outBag) { 2726 lock(); 2727 2728 int err = getBagLocked(resID, outBag, null); 2729 if (err < NO_ERROR) { 2730 //printf("*** get failed! unlocking\n"); 2731 mLock.release(); 2732 } 2733 return err; 2734 } 2735 getBagLocked(int resID, Ref<bag_entry[]> outBag, Ref<Integer> outTypeSpecFlags)2736 public int getBagLocked(int resID, Ref<bag_entry[]> outBag, Ref<Integer> outTypeSpecFlags) { 2737 if (mError != NO_ERROR) { 2738 return mError; 2739 } 2740 2741 final int p = getResourcePackageIndex(resID); 2742 final int t = Res_GETTYPE(resID); 2743 final int e = Res_GETENTRY(resID); 2744 2745 if (p < 0) { 2746 ALOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID); 2747 return BAD_INDEX; 2748 } 2749 if (t < 0) { 2750 ALOGW("No type identifier when getting bag for resource number 0x%08x", resID); 2751 return BAD_INDEX; 2752 } 2753 2754 //printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t); 2755 PackageGroup grp = mPackageGroups.get(p); 2756 if (grp == NULL) { 2757 ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID); 2758 return BAD_INDEX; 2759 } 2760 2761 final List<Type> typeConfigs = getOrDefault(grp.types, t, Collections.emptyList()); 2762 if (typeConfigs.isEmpty()) { 2763 ALOGW("Type identifier 0x%x does not exist.", t+1); 2764 return BAD_INDEX; 2765 } 2766 2767 final int NENTRY = typeConfigs.get(0).entryCount; 2768 if (e >= (int)NENTRY) { 2769 ALOGW("Entry identifier 0x%x is larger than entry count 0x%x", 2770 e, (int)typeConfigs.get(0).entryCount); 2771 return BAD_INDEX; 2772 } 2773 2774 // First see if we've already computed this bag... 2775 TypeCacheEntry cacheEntry = grp.typeCacheEntries.editItemAt(t); 2776 bag_set[] typeSet = cacheEntry.cachedBags; 2777 // todo cache 2778 // if (isTruthy(typeSet)) { 2779 // bag_set set = typeSet[e]; 2780 // if (isTruthy(set)) { 2781 // if (set != (bag_set) 0xFFFFFFFF){ 2782 // if (set != SENTINEL_BAG_SET){ 2783 // if (outTypeSpecFlags != NULL) { 2784 // outTypeSpecFlags.set(set.typeSpecFlags); 2785 // } 2786 // outBag.set((bag_entry *) (set + 1); 2787 // if (kDebugTableSuperNoisy) { 2788 // ALOGI("Found existing bag for: 0x%x\n", resID); 2789 // } 2790 // return set.numAttrs; 2791 // } 2792 // ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.", 2793 // resID); 2794 // return BAD_INDEX; 2795 // } 2796 // } 2797 // 2798 // Bag not found, we need to compute it! 2799 if (!isTruthy(typeSet)) { 2800 typeSet = new bag_set[NENTRY]; // (bag_set**)calloc(NENTRY, sizeof(bag_set*)); 2801 //cacheEntry.cachedBags = typeSet; 2802 } 2803 // 2804 // // Mark that we are currently working on this one. 2805 // typeSet[e] = (bag_set*)0xFFFFFFFF; 2806 // typeSet[e] = SENTINEL_BAG_SET; 2807 2808 if (kDebugTableNoisy) { 2809 ALOGI("Building bag: %x\n", resID); 2810 } 2811 2812 // Now collect all bag attributes 2813 Entry entry = new Entry(); 2814 int err = getEntry(grp, t, e, mParams, entry); 2815 if (err != NO_ERROR) { 2816 return err; 2817 } 2818 final short entrySize = dtohs(entry.entry.size); 2819 // const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) 2820 // ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0; 2821 // const uint32_t count = entrySize >= sizeof(ResTable_map_entry) 2822 // ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0; 2823 ResTable_map_entry mapEntry = entrySize >= ResTable_map_entry.BASE_SIZEOF ? 2824 new ResTable_map_entry(entry.entry.myBuf(), entry.entry.myOffset()) : null; 2825 final int parent = mapEntry != null ? dtohl(mapEntry.parent.ident) : 0; 2826 final int count = mapEntry != null ? dtohl(mapEntry.count) : 0; 2827 2828 int N = count; 2829 2830 if (kDebugTableNoisy) { 2831 ALOGI("Found map: size=%x parent=%x count=%d\n", entrySize, parent, count); 2832 2833 // If this map inherits from another, we need to start 2834 // with its parent's values. Otherwise start out empty. 2835 ALOGI("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", entrySize, parent); 2836 } 2837 2838 // This is what we are building. 2839 bag_set set; 2840 2841 if (isTruthy(parent)) { 2842 final Ref<Integer> resolvedParent = new Ref<>(parent); 2843 2844 // Bags encode a parent reference without using the standard 2845 // Res_value structure. That means we must always try to 2846 // resolve a parent reference in case it is actually a 2847 // TYPE_DYNAMIC_REFERENCE. 2848 err = grp.dynamicRefTable.lookupResourceId(resolvedParent); 2849 if (err != NO_ERROR) { 2850 ALOGE("Failed resolving bag parent id 0x%08x", parent); 2851 return UNKNOWN_ERROR; 2852 } 2853 2854 final Ref<bag_entry[]> parentBag = new Ref<>(null); 2855 final Ref<Integer> parentTypeSpecFlags = new Ref<>(0); 2856 final int NP = getBagLocked(resolvedParent.get(), parentBag, parentTypeSpecFlags); 2857 final int NT = ((NP >= 0) ? NP : 0) + N; 2858 set = new bag_set(NT); 2859 if (NP > 0) { 2860 set.copyFrom(parentBag.get(), NP); 2861 set.numAttrs = NP; 2862 if (kDebugTableNoisy) { 2863 ALOGI("Initialized new bag with %d inherited attributes.\n", NP); 2864 } 2865 } else { 2866 if (kDebugTableNoisy) { 2867 ALOGI("Initialized new bag with no inherited attributes.\n"); 2868 } 2869 set.numAttrs = 0; 2870 } 2871 set.availAttrs = NT; 2872 set.typeSpecFlags = parentTypeSpecFlags.get(); 2873 } else { 2874 set = new bag_set(N); 2875 set.numAttrs = 0; 2876 set.availAttrs = N; 2877 set.typeSpecFlags = 0; 2878 } 2879 2880 set.typeSpecFlags |= entry.specFlags; 2881 2882 // Now merge in the new attributes... 2883 // int curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type)) 2884 // + dtohs(entry.entry.size); 2885 int curOff = entry.entry.myOffset() - entry.type.myOffset() + entry.entry.size; 2886 ResTable_map map; 2887 // bag_entry* entries = (bag_entry*)(set+1); 2888 bag_entry[] entries = set.bag_entries; 2889 int curEntry = 0; 2890 int pos = 0; 2891 if (kDebugTableNoisy) { 2892 ALOGI("Starting with set %s, entries=%s, avail=0x%x\n", set, entries, set.availAttrs); 2893 } 2894 while (pos < count) { 2895 if (kDebugTableNoisy) { 2896 // ALOGI("Now at %s\n", curOff); 2897 ALOGI("Now at %s\n", curEntry); 2898 } 2899 2900 if (curOff > (dtohl(entry.type.header.size)- ResTable_map.SIZEOF)) { 2901 ALOGW("ResTable_map at %d is beyond type chunk data %d", 2902 (int)curOff, dtohl(entry.type.header.size)); 2903 return BAD_TYPE; 2904 } 2905 // map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff); 2906 map = new ResTable_map(entry.type.myBuf(), entry.type.myOffset() + curOff); 2907 N++; 2908 2909 final Ref<Integer> newName = new Ref<>(htodl(map.name.ident)); 2910 if (!Res_INTERNALID(newName.get())) { 2911 // Attributes don't have a resource id as the name. They specify 2912 // other data, which would be wrong to change via a lookup. 2913 if (grp.dynamicRefTable.lookupResourceId(newName) != NO_ERROR) { 2914 ALOGE("Failed resolving ResTable_map name at %d with ident 0x%08x", 2915 (int) curEntry, (int) newName.get()); 2916 return UNKNOWN_ERROR; 2917 } 2918 } 2919 2920 boolean isInside; 2921 int oldName = 0; 2922 while ((isInside=(curEntry < set.numAttrs)) 2923 && (oldName=entries[curEntry].map.name.ident) < newName.get()) { 2924 if (kDebugTableNoisy) { 2925 ALOGI("#0x%x: Keeping existing attribute: 0x%08x\n", 2926 curEntry, entries[curEntry].map.name.ident); 2927 } 2928 curEntry++; 2929 } 2930 2931 if (!isInside || oldName != newName.get()) { 2932 // This is a new attribute... figure out what to do with it. 2933 if (set.numAttrs >= set.availAttrs) { 2934 // Need to alloc more memory... 2935 final int newAvail = set.availAttrs+N; 2936 // set = (bag_set[])realloc(set, 2937 // sizeof(bag_set) 2938 // + sizeof(bag_entry)*newAvail); 2939 set.resizeBagEntries(newAvail); 2940 set.availAttrs = newAvail; 2941 // entries = (bag_entry*)(set+1); 2942 entries = set.bag_entries; 2943 if (kDebugTableNoisy) { 2944 ALOGI("Reallocated set %s, entries=%s, avail=0x%x\n", 2945 set, entries, set.availAttrs); 2946 } 2947 } 2948 if (isInside) { 2949 // Going in the middle, need to make space. 2950 // memmove(entries+curEntry+1, entries+curEntry, 2951 // sizeof(bag_entry)*(set.numAttrs-curEntry)); 2952 System.arraycopy(entries, curEntry, entries, curEntry + 1, set.numAttrs - curEntry); 2953 entries[curEntry] = null; 2954 set.numAttrs++; 2955 } 2956 if (kDebugTableNoisy) { 2957 ALOGI("#0x%x: Inserting new attribute: 0x%08x\n", curEntry, newName.get()); 2958 } 2959 } else { 2960 if (kDebugTableNoisy) { 2961 ALOGI("#0x%x: Replacing existing attribute: 0x%08x\n", curEntry, oldName); 2962 } 2963 } 2964 2965 bag_entry cur = entries[curEntry]; 2966 if (cur == null) { 2967 cur = entries[curEntry] = new bag_entry(); 2968 } 2969 2970 cur.stringBlock = entry._package_.header.index; 2971 cur.map.name.ident = newName.get(); 2972 // cur->map.value.copyFrom_dtoh(map->value); 2973 cur.map.value = map.value; 2974 final Ref<Res_value> valueRef = new Ref<>(cur.map.value); 2975 err = grp.dynamicRefTable.lookupResourceValue(valueRef); 2976 cur.map.value = map.value = valueRef.get(); 2977 if (err != NO_ERROR) { 2978 ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur.map.value.data); 2979 return UNKNOWN_ERROR; 2980 } 2981 2982 if (kDebugTableNoisy) { 2983 ALOGI("Setting entry #0x%x %s: block=%d, name=0x%08d, type=%d, data=0x%08x\n", 2984 curEntry, cur, cur.stringBlock, cur.map.name.ident, 2985 cur.map.value.dataType, cur.map.value.data); 2986 } 2987 2988 // On to the next! 2989 curEntry++; 2990 pos++; 2991 final int size = dtohs(map.value.size); 2992 // curOff += size + sizeof(*map)-sizeof(map->value); 2993 curOff += size + ResTable_map.SIZEOF-Res_value.SIZEOF; 2994 }; 2995 2996 if (curEntry > set.numAttrs) { 2997 set.numAttrs = curEntry; 2998 } 2999 3000 // And this is it... 3001 typeSet[e] = set; 3002 if (isTruthy(set)) { 3003 if (outTypeSpecFlags != NULL) { 3004 outTypeSpecFlags.set(set.typeSpecFlags); 3005 } 3006 outBag.set(set.bag_entries); 3007 if (kDebugTableNoisy) { 3008 ALOGI("Returning 0x%x attrs\n", set.numAttrs); 3009 } 3010 return set.numAttrs; 3011 } 3012 return BAD_INDEX; 3013 } 3014 unlockBag(Ref<bag_entry[]> bag)3015 public void unlockBag(Ref<bag_entry[]> bag) { 3016 unlock(); 3017 } 3018 3019 static class bag_set { 3020 int numAttrs; // number in array 3021 int availAttrs; // total space in array 3022 int typeSpecFlags; 3023 // Followed by 'numAttr' bag_entry structures. 3024 3025 bag_entry[] bag_entries; 3026 bag_set(int entryCount)3027 public bag_set(int entryCount) { 3028 bag_entries = new bag_entry[entryCount]; 3029 } 3030 copyFrom(bag_entry[] parentBag, int count)3031 public void copyFrom(bag_entry[] parentBag, int count) { 3032 for (int i = 0; i < count; i++) { 3033 bag_entries[i] = parentBag[i]; 3034 } 3035 } 3036 resizeBagEntries(int newEntryCount)3037 public void resizeBagEntries(int newEntryCount) { 3038 bag_entry[] newEntries = new bag_entry[newEntryCount]; 3039 System.arraycopy(bag_entries, 0, newEntries, 0, Math.min(bag_entries.length, newEntryCount)); 3040 bag_entries = newEntries; 3041 } 3042 }; 3043 3044 /** 3045 * Configuration dependent cached data. This must be cleared when the configuration is 3046 * changed (setParameters). 3047 */ 3048 static class TypeCacheEntry { 3049 // TypeCacheEntry() : cachedBags(NULL) {} 3050 3051 // Computed attribute bags for this type. 3052 // bag_set** cachedBags; 3053 bag_set[] cachedBags; 3054 3055 // Pre-filtered list of configurations (per asset path) that match the parameters set on this 3056 // ResTable. 3057 List<List<ResTable_type>> filteredConfigs; 3058 }; 3059 3060 Res_MAKEID(int packageId, int typeId, int entryId)3061 private int Res_MAKEID(int packageId, int typeId, int entryId) { 3062 return (((packageId+1)<<24) | (((typeId+1)&0xFF)<<16) | (entryId&0xFFFF)); 3063 } 3064 3065 // struct resource_name 3066 public static class ResourceName { 3067 public String packageName; 3068 public String type; 3069 public String name; 3070 3071 @Override toString()3072 public String toString() { 3073 return packageName.trim() + '@' + type + ':' + name; 3074 } 3075 } 3076 3077 private interface Function<K, V> { 3078 V apply(K key); 3079 } 3080 computeIfAbsent(Map<K, V> map, K key, Function<K, V> vFunction)3081 static <K, V> V computeIfAbsent(Map<K, V> map, K key, Function<K, V> vFunction) { 3082 V v = map.get(key); 3083 if (v == null) { 3084 v = vFunction.apply(key); 3085 map.put(key, v); 3086 } 3087 return v; 3088 } 3089 getOrDefault(Map<K, V> map, K key, V defaultValue)3090 static <K, V> V getOrDefault(Map<K, V> map, K key, V defaultValue) { 3091 V v; 3092 return (((v = map.get(key)) != null) || map.containsKey(key)) ? v : defaultValue; 3093 } 3094 } 3095