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