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