1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.M; 4 import static android.os.Build.VERSION_CODES.N_MR1; 5 import static android.os.Build.VERSION_CODES.O; 6 import static android.os.Build.VERSION_CODES.O_MR1; 7 import static android.os.Build.VERSION_CODES.P; 8 import static org.robolectric.res.android.Asset.SEEK_CUR; 9 import static org.robolectric.res.android.Asset.SEEK_SET; 10 import static org.robolectric.res.android.AttributeResolution.kThrowOnBadId; 11 import static org.robolectric.res.android.Errors.BAD_INDEX; 12 import static org.robolectric.res.android.Errors.NO_ERROR; 13 import static org.robolectric.res.android.Util.ALOGV; 14 import static org.robolectric.res.android.Util.isTruthy; 15 import static org.robolectric.util.reflector.Reflector.reflector; 16 17 import android.content.res.AssetManager; 18 import android.os.Build.VERSION_CODES; 19 import android.os.ParcelFileDescriptor; 20 import android.util.SparseArray; 21 import android.util.TypedValue; 22 import com.google.common.annotations.VisibleForTesting; 23 import com.google.common.base.Preconditions; 24 import com.google.common.base.Strings; 25 import dalvik.system.VMRuntime; 26 import java.io.FileDescriptor; 27 import java.io.FileNotFoundException; 28 import java.io.IOException; 29 import java.nio.file.Files; 30 import java.nio.file.Path; 31 import java.util.ArrayList; 32 import java.util.Collection; 33 import java.util.HashMap; 34 import java.util.List; 35 import java.util.Map; 36 import org.robolectric.RuntimeEnvironment; 37 import org.robolectric.annotation.HiddenApi; 38 import org.robolectric.annotation.Implementation; 39 import org.robolectric.annotation.Implements; 40 import org.robolectric.annotation.RealObject; 41 import org.robolectric.annotation.Resetter; 42 import org.robolectric.res.Fs; 43 import org.robolectric.res.android.Asset; 44 import org.robolectric.res.android.Asset.AccessMode; 45 import org.robolectric.res.android.AssetDir; 46 import org.robolectric.res.android.AssetPath; 47 import org.robolectric.res.android.AttributeResolution; 48 import org.robolectric.res.android.CppAssetManager; 49 import org.robolectric.res.android.DataType; 50 import org.robolectric.res.android.DynamicRefTable; 51 import org.robolectric.res.android.Ref; 52 import org.robolectric.res.android.Registries; 53 import org.robolectric.res.android.ResStringPool; 54 import org.robolectric.res.android.ResTable; 55 import org.robolectric.res.android.ResTable.ResourceName; 56 import org.robolectric.res.android.ResTable.bag_entry; 57 import org.robolectric.res.android.ResTableTheme; 58 import org.robolectric.res.android.ResTable_config; 59 import org.robolectric.res.android.ResXMLParser; 60 import org.robolectric.res.android.ResXMLTree; 61 import org.robolectric.res.android.ResourceTypes.Res_value; 62 import org.robolectric.res.android.String8; 63 import org.robolectric.shadow.api.Shadow; 64 import org.robolectric.shadows.ShadowAssetManager.Picker; 65 66 // native method impls transliterated from 67 // https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/core/jni/android_util_AssetManager.cpp 68 @Implements(value = AssetManager.class, maxSdk = VERSION_CODES.O_MR1, shadowPicker = Picker.class) 69 @SuppressWarnings("NewApi") 70 public class ShadowArscAssetManager extends ShadowAssetManager.ArscBase { 71 72 private static final int STYLE_NUM_ENTRIES = 6; 73 private static final int STYLE_TYPE = 0; 74 private static final int STYLE_DATA = 1; 75 private static final int STYLE_ASSET_COOKIE = 2; 76 private static final int STYLE_RESOURCE_ID = 3; 77 private static final int STYLE_CHANGING_CONFIGURATIONS = 4; 78 private static final int STYLE_DENSITY = 5; 79 80 @RealObject protected AssetManager realObject; 81 82 private CppAssetManager cppAssetManager; 83 84 @Resetter reset()85 public static void reset() { 86 // todo: ShadowPicker doesn't discriminate properly between concrete shadow classes for 87 // resetters... 88 if (RuntimeEnvironment.getApiLevel() < P) { 89 reflector(_AssetManager_.class).setSystem(null); 90 // NATIVE_THEME_REGISTRY.clear(); 91 // nativeXMLParserRegistry.clear(); // todo: shouldn't these be freed explicitly? [yes! xw] 92 // NATIVE_ASSET_REGISTRY.clear(); 93 } 94 } 95 96 @Implementation list(String path)97 protected String[] list(String path) throws IOException { 98 CppAssetManager am = assetManagerForJavaObject(); 99 100 String fileName8 = path; 101 if (fileName8 == null) { 102 return null; 103 } 104 105 AssetDir dir = am.openDir(fileName8); 106 107 if (dir == null) { 108 throw new FileNotFoundException(fileName8); 109 } 110 111 int N = dir.getFileCount(); 112 113 String[] array = new String[dir.getFileCount()]; 114 115 for (int i = 0; i < N; i++) { 116 String8 name = dir.getFileName(i); 117 array[i] = name.string(); 118 } 119 120 return array; 121 } 122 123 // @HiddenApi @Implementation(minSdk = VERSION_CODES.P) 124 // public void setApkAssets(Object apkAssetsObjects, Object invalidateCaches) { 125 // throw new UnsupportedOperationException("implement me"); 126 // } 127 128 @HiddenApi 129 @Implementation(maxSdk = N_MR1) setConfiguration( int mcc, int mnc, String locale, int orientation, int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int sdkVersion)130 public final void setConfiguration( 131 int mcc, 132 int mnc, 133 String locale, 134 int orientation, 135 int touchscreen, 136 int density, 137 int keyboard, 138 int keyboardHidden, 139 int navigation, 140 int screenWidth, 141 int screenHeight, 142 int smallestScreenWidthDp, 143 int screenWidthDp, 144 int screenHeightDp, 145 int screenLayout, 146 int uiMode, 147 int sdkVersion) { 148 setConfiguration( 149 mcc, 150 mnc, 151 locale, 152 orientation, 153 touchscreen, 154 density, 155 keyboard, 156 keyboardHidden, 157 navigation, 158 screenWidth, 159 screenHeight, 160 smallestScreenWidthDp, 161 screenWidthDp, 162 screenHeightDp, 163 screenLayout, 164 uiMode, 165 0, 166 sdkVersion); 167 } 168 169 @HiddenApi 170 @Implementation(minSdk = O) setConfiguration( int mcc, int mnc, String locale, int orientation, int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode, int sdkVersion)171 public void setConfiguration( 172 int mcc, 173 int mnc, 174 String locale, 175 int orientation, 176 int touchscreen, 177 int density, 178 int keyboard, 179 int keyboardHidden, 180 int navigation, 181 int screenWidth, 182 int screenHeight, 183 int smallestScreenWidthDp, 184 int screenWidthDp, 185 int screenHeightDp, 186 int screenLayout, 187 int uiMode, 188 int colorMode, 189 int sdkVersion) { 190 CppAssetManager am = assetManagerForJavaObject(); 191 if (am == null) { 192 return; 193 } 194 195 ResTable_config config = new ResTable_config(); 196 // memset(&config, 0, sizeof(config)); 197 198 // const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; 199 200 // Constants duplicated from Java class android.content.res.Configuration. 201 int kScreenLayoutRoundMask = 0x300; 202 int kScreenLayoutRoundShift = 8; 203 204 config.mcc = mcc; 205 config.mnc = mnc; 206 config.orientation = orientation; 207 config.touchscreen = touchscreen; 208 config.density = density; 209 config.keyboard = keyboard; 210 config.inputFlags = keyboardHidden; 211 config.navigation = navigation; 212 config.screenWidth = screenWidth; 213 config.screenHeight = screenHeight; 214 config.smallestScreenWidthDp = smallestScreenWidthDp; 215 config.screenWidthDp = screenWidthDp; 216 config.screenHeightDp = screenHeightDp; 217 config.screenLayout = screenLayout; 218 config.uiMode = uiMode; 219 config.colorMode = (byte) colorMode; 220 config.sdkVersion = sdkVersion; 221 config.minorVersion = 0; 222 223 // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer 224 // in C++. We must extract the round qualifier out of the Java screenLayout and put it 225 // into screenLayout2. 226 config.screenLayout2 = 227 (byte) ((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); 228 229 am.setConfiguration(config, locale); 230 231 // if (locale != null) env->ReleaseStringUTFChars(locale, locale8); 232 } 233 234 @HiddenApi 235 @Implementation dumpTheme(long theme, int priority, String tag, String prefix)236 protected static void dumpTheme(long theme, int priority, String tag, String prefix) { 237 throw new UnsupportedOperationException("not yet implemented"); 238 } 239 240 @Implementation getResourceName(int resid)241 protected String getResourceName(int resid) { 242 CppAssetManager am = assetManagerForJavaObject(); 243 244 ResourceName name = new ResourceName(); 245 if (!am.getResources().getResourceName(resid, true, name)) { 246 return null; 247 } 248 249 StringBuilder str = new StringBuilder(); 250 if (name.packageName != null) { 251 str.append(name.packageName.trim()); 252 } 253 if (name.type != null) { 254 if (str.length() > 0) { 255 char div = ':'; 256 str.append(div); 257 } 258 str.append(name.type); 259 } 260 if (name.name != null) { 261 if (str.length() > 0) { 262 char div = '/'; 263 str.append(div); 264 } 265 str.append(name.name); 266 } 267 return str.toString(); 268 } 269 270 @Implementation getResourcePackageName(int resid)271 protected String getResourcePackageName(int resid) { 272 CppAssetManager cppAssetManager = assetManagerForJavaObject(); 273 274 ResourceName name = new ResourceName(); 275 if (!cppAssetManager.getResources().getResourceName(resid, true, name)) { 276 return null; 277 } 278 279 return name.packageName.trim(); 280 } 281 282 @Implementation getResourceTypeName(int resid)283 protected String getResourceTypeName(int resid) { 284 CppAssetManager cppAssetManager = assetManagerForJavaObject(); 285 286 ResourceName name = new ResourceName(); 287 if (!cppAssetManager.getResources().getResourceName(resid, true, name)) { 288 return null; 289 } 290 291 return name.type; 292 } 293 294 @Implementation getResourceEntryName(int resid)295 protected String getResourceEntryName(int resid) { 296 CppAssetManager cppAssetManager = assetManagerForJavaObject(); 297 298 ResourceName name = new ResourceName(); 299 if (!cppAssetManager.getResources().getResourceName(resid, true, name)) { 300 return null; 301 } 302 303 return name.name; 304 } 305 306 //////////// native method implementations 307 308 // public native final String[] list(String path) 309 // throws IOException; 310 311 // @HiddenApi @Implementation(minSdk = VERSION_CODES.P) 312 // public void setApkAssets(Object apkAssetsObjects, Object invalidateCaches) { 313 // throw new UnsupportedOperationException("implement me"); 314 // } 315 // 316 317 @HiddenApi 318 @Implementation(maxSdk = M) addAssetPathNative(String path)319 protected final int addAssetPathNative(String path) { 320 return addAssetPathNative(path, false); 321 } 322 323 @HiddenApi 324 @Implementation(minSdk = VERSION_CODES.N) addAssetPathNative(String path, boolean appAsLib)325 protected int addAssetPathNative(String path, boolean appAsLib) { 326 if (Strings.isNullOrEmpty(path)) { 327 return 0; 328 } 329 330 CppAssetManager am = assetManagerForJavaObject(); 331 if (am == null) { 332 return 0; 333 } 334 final Ref<Integer> cookie = new Ref<>(null); 335 boolean res = am.addAssetPath(new String8(path), cookie, appAsLib); 336 return (res) ? cookie.get() : 0; 337 } 338 339 @HiddenApi 340 @Implementation getResourceIdentifier(String name, String defType, String defPackage)341 public int getResourceIdentifier(String name, String defType, String defPackage) { 342 if (Strings.isNullOrEmpty(name)) { 343 return 0; 344 } 345 CppAssetManager am = assetManagerForJavaObject(); 346 if (am == null) { 347 return 0; 348 } 349 350 int ident = am.getResources().identifierForName(name, defType, defPackage); 351 352 return ident; 353 } 354 355 @HiddenApi 356 @Implementation openAsset(String fileName, int mode)357 protected long openAsset(String fileName, int mode) throws FileNotFoundException { 358 CppAssetManager am = assetManagerForJavaObject(); 359 360 ALOGV("openAsset in %s", am); 361 362 String fileName8 = fileName; 363 if (fileName8 == null) { 364 throw new IllegalArgumentException("Empty file name"); 365 } 366 367 if (mode != AccessMode.ACCESS_UNKNOWN.mode() 368 && mode != AccessMode.ACCESS_RANDOM.mode() 369 && mode != AccessMode.ACCESS_STREAMING.mode() 370 && mode != AccessMode.ACCESS_BUFFER.mode()) { 371 throw new IllegalArgumentException("Bad access mode"); 372 } 373 374 Asset a = am.open(fileName8, AccessMode.fromInt(mode)); 375 376 if (a == null) { 377 throw new FileNotFoundException(fileName8); 378 } 379 380 // printf("Created Asset Stream: %p\n", a); 381 382 return Registries.NATIVE_ASSET_REGISTRY.register(a); 383 } 384 385 @HiddenApi 386 @Implementation openAssetFd(String fileName, long[] outOffsets)387 protected ParcelFileDescriptor openAssetFd(String fileName, long[] outOffsets) 388 throws IOException { 389 CppAssetManager am = assetManagerForJavaObject(); 390 391 ALOGV("openAssetFd in %s", am); 392 393 String fileName8 = fileName; 394 if (fileName8 == null) { 395 return null; 396 } 397 398 Asset a = am.open(fileName8, Asset.AccessMode.ACCESS_RANDOM); 399 400 if (a == null) { 401 throw new FileNotFoundException(fileName8); 402 } 403 404 return returnParcelFileDescriptor(a, outOffsets); 405 } 406 407 @HiddenApi 408 @Implementation openNonAssetNative(int cookie, String fileName, int accessMode)409 protected long openNonAssetNative(int cookie, String fileName, int accessMode) 410 throws FileNotFoundException { 411 CppAssetManager am = assetManagerForJavaObject(); 412 if (am == null) { 413 return 0; 414 } 415 ALOGV("openNonAssetNative in %s (Java object %s)\n", am, AssetManager.class); 416 String fileName8 = fileName; 417 if (fileName8 == null) { 418 return -1; 419 } 420 AccessMode mode = AccessMode.fromInt(accessMode); 421 if (mode != Asset.AccessMode.ACCESS_UNKNOWN 422 && mode != Asset.AccessMode.ACCESS_RANDOM 423 && mode != Asset.AccessMode.ACCESS_STREAMING 424 && mode != Asset.AccessMode.ACCESS_BUFFER) { 425 throw new IllegalArgumentException("Bad access mode"); 426 } 427 Asset a = 428 isTruthy(cookie) 429 ? am.openNonAsset(cookie, fileName8, mode) 430 : am.openNonAsset(fileName8, mode, null); 431 if (a == null) { 432 throw new FileNotFoundException(fileName8); 433 } 434 long assetId = Registries.NATIVE_ASSET_REGISTRY.register(a); 435 // todo: something better than this [xw] 436 a.onClose = () -> destroyAsset(assetId); 437 // printf("Created Asset Stream: %p\n", a); 438 return assetId; 439 } 440 441 @HiddenApi 442 @Implementation openNonAssetFdNative( int cookie, String fileName, long[] outOffsets)443 protected ParcelFileDescriptor openNonAssetFdNative( 444 int cookie, String fileName, long[] outOffsets) throws IOException { 445 CppAssetManager am = assetManagerForJavaObject(); 446 447 ALOGV("openNonAssetFd in %s (Java object %s)", am, this); 448 449 if (fileName == null) { 450 return null; 451 } 452 453 Asset a = 454 isTruthy(cookie) 455 ? am.openNonAsset(cookie, fileName, Asset.AccessMode.ACCESS_RANDOM) 456 : am.openNonAsset(fileName, Asset.AccessMode.ACCESS_RANDOM, null); 457 458 if (a == null) { 459 throw new FileNotFoundException(fileName); 460 } 461 462 // printf("Created Asset Stream: %p\n", a); 463 464 return returnParcelFileDescriptor(a, outOffsets); 465 } 466 467 @HiddenApi 468 @Implementation destroyAsset(long asset)469 protected void destroyAsset(long asset) { 470 Registries.NATIVE_ASSET_REGISTRY.unregister(asset); 471 } 472 473 @HiddenApi 474 @Implementation readAssetChar(long asset)475 protected int readAssetChar(long asset) { 476 Asset a = getAsset(asset); 477 byte[] b = new byte[1]; 478 int res = a.read(b, 1); 479 return res == 1 ? b[0] & 0xff : -1; 480 } 481 482 @HiddenApi 483 @Implementation readAsset(long asset, byte[] bArray, int off, int len)484 protected int readAsset(long asset, byte[] bArray, int off, int len) throws IOException { 485 Asset a = getAsset(asset); 486 487 if (a == null || bArray == null) { 488 throw new NullPointerException("asset"); 489 } 490 491 if (len == 0) { 492 return 0; 493 } 494 495 int bLen = bArray.length; 496 if (off < 0 || off >= bLen || len < 0 || len > bLen || (off + len) > bLen) { 497 throw new IndexOutOfBoundsException(); 498 } 499 500 byte[] b = bArray; 501 int res = a.read(b, off, len); 502 503 if (res > 0) return res; 504 505 if (res < 0) { 506 throw new IOException(); 507 } 508 return -1; 509 } 510 511 @HiddenApi 512 @Implementation seekAsset(long asset, long offset, int whence)513 protected long seekAsset(long asset, long offset, int whence) { 514 Asset a = getAsset(asset); 515 return a.seek(offset, whence < 0 ? SEEK_SET : SEEK_CUR); 516 } 517 518 @HiddenApi 519 @Implementation getAssetLength(long asset)520 protected long getAssetLength(long asset) { 521 Asset a = getAsset(asset); 522 return a.getLength(); 523 } 524 525 @HiddenApi 526 @Implementation getAssetRemainingLength(long assetHandle)527 protected long getAssetRemainingLength(long assetHandle) { 528 Asset a = getAsset(assetHandle); 529 530 if (a == null) { 531 throw new NullPointerException("asset"); 532 } 533 534 return a.getRemainingLength(); 535 } 536 getAsset(long asset)537 private Asset getAsset(long asset) { 538 return Registries.NATIVE_ASSET_REGISTRY.getNativeObject(asset); 539 } 540 541 @HiddenApi 542 @Implementation loadResourceValue(int ident, short density, TypedValue outValue, boolean resolve)543 protected int loadResourceValue(int ident, short density, TypedValue outValue, boolean resolve) { 544 if (outValue == null) { 545 throw new NullPointerException("outValue"); 546 // return 0; 547 } 548 CppAssetManager am = assetManagerForJavaObject(); 549 if (am == null) { 550 return 0; 551 } 552 final ResTable res = am.getResources(); 553 554 final Ref<Res_value> value = new Ref<>(null); 555 final Ref<ResTable_config> config = new Ref<>(null); 556 final Ref<Integer> typeSpecFlags = new Ref<>(null); 557 int block = res.getResource(ident, value, false, density, typeSpecFlags, config); 558 if (kThrowOnBadId) { 559 if (block == BAD_INDEX) { 560 throw new IllegalStateException("Bad resource!"); 561 // return 0; 562 } 563 } 564 final Ref<Integer> ref = new Ref<>(ident); 565 if (resolve) { 566 block = res.resolveReference(value, block, ref, typeSpecFlags, config); 567 if (kThrowOnBadId) { 568 if (block == BAD_INDEX) { 569 throw new IllegalStateException("Bad resource!"); 570 // return 0; 571 } 572 } 573 } 574 if (block >= 0) { 575 // return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config); 576 return copyValue( 577 outValue, res, value.get(), ref.get(), block, typeSpecFlags.get(), config.get()); 578 } 579 return block; 580 } 581 copyValue( TypedValue outValue, ResTable table, Res_value value, int ref, int block, int typeSpecFlags)582 private static int copyValue( 583 TypedValue outValue, ResTable table, Res_value value, int ref, int block, int typeSpecFlags) { 584 return copyValue(outValue, table, value, ref, block, typeSpecFlags, null); 585 } 586 copyValue( TypedValue outValue, ResTable table, Res_value value, int ref, int block, int typeSpecFlags, ResTable_config config)587 private static int copyValue( 588 TypedValue outValue, 589 ResTable table, 590 Res_value value, 591 int ref, 592 int block, 593 int typeSpecFlags, 594 ResTable_config config) { 595 outValue.type = value.dataType; 596 outValue.assetCookie = table.getTableCookie(block); 597 outValue.data = value.data; 598 outValue.string = null; 599 outValue.resourceId = ref; 600 outValue.changingConfigurations = typeSpecFlags; 601 602 if (config != null) { 603 outValue.density = config.density; 604 } 605 return block; 606 } 607 getResourceBagValues(int ident, ResTable res)608 public static Map<String, Integer> getResourceBagValues(int ident, ResTable res) { 609 // Now lock down the resource object and start pulling stuff from it. 610 res.lock(); 611 612 HashMap<String, Integer> map; 613 try { 614 final Ref<bag_entry[]> entryRef = new Ref<>(null); 615 final Ref<Integer> typeSpecFlags = new Ref<>(0); 616 int entryCount = res.getBagLocked(ident, entryRef, typeSpecFlags); 617 618 map = new HashMap<>(); 619 bag_entry[] bag_entries = entryRef.get(); 620 for (int i = 0; i < entryCount; i++) { 621 bag_entry entry = bag_entries[i]; 622 ResourceName resourceName = new ResourceName(); 623 if (res.getResourceName(entry.map.name.ident, true, resourceName)) { 624 map.put(resourceName.name, entry.map.value.data); 625 } 626 } 627 } finally { 628 res.unlock(); 629 } 630 631 return map; 632 } 633 634 /** Returns true if the resource was found, filling in mRetStringBlock and mRetData. */ 635 @Implementation 636 @HiddenApi loadResourceBagValue( int ident, int bagEntryId, TypedValue outValue, boolean resolve)637 protected int loadResourceBagValue( 638 int ident, int bagEntryId, TypedValue outValue, boolean resolve) { 639 CppAssetManager am = assetManagerForJavaObject(); 640 if (am == null) { 641 return 0; 642 } 643 final ResTable res = am.getResources(); 644 return loadResourceBagValueInternal(ident, bagEntryId, outValue, resolve, res); 645 } 646 getResourceBagValue(int ident, int bagEntryId, ResTable resTable)647 public static String getResourceBagValue(int ident, int bagEntryId, ResTable resTable) { 648 TypedValue outValue = new TypedValue(); 649 int blockId = 650 ShadowArscAssetManager.loadResourceBagValueInternal( 651 ident, bagEntryId, outValue, true, resTable); 652 if (outValue.type == TypedValue.TYPE_STRING) { 653 return resTable.getTableStringBlock(blockId).stringAt(outValue.data); 654 } else { 655 return outValue.coerceToString().toString(); 656 } 657 } 658 loadResourceBagValueInternal( int ident, int bagEntryId, TypedValue outValue, boolean resolve, ResTable res)659 private static int loadResourceBagValueInternal( 660 int ident, int bagEntryId, TypedValue outValue, boolean resolve, ResTable res) { 661 // Now lock down the resource object and start pulling stuff from it. 662 res.lock(); 663 664 int block = -1; 665 final Ref<Res_value> valueRef = new Ref<>(null); 666 final Ref<bag_entry[]> entryRef = new Ref<>(null); 667 final Ref<Integer> typeSpecFlags = new Ref<>(0); 668 int entryCount = res.getBagLocked(ident, entryRef, typeSpecFlags); 669 670 bag_entry[] bag_entries = entryRef.get(); 671 for (int i = 0; i < entryCount; i++) { 672 bag_entry entry = bag_entries[i]; 673 if (bagEntryId == entry.map.name.ident) { 674 block = entry.stringBlock; 675 valueRef.set(entry.map.value); 676 } 677 } 678 679 res.unlock(); 680 681 if (block < 0) { 682 return block; 683 } 684 685 final Ref<Integer> ref = new Ref<>(ident); 686 if (resolve) { 687 block = res.resolveReference(valueRef, block, ref, typeSpecFlags); 688 if (kThrowOnBadId) { 689 if (block == BAD_INDEX) { 690 throw new IllegalStateException("Bad resource!"); 691 } 692 } 693 } 694 if (block >= 0) { 695 return copyValue(outValue, res, valueRef.get(), ref.get(), block, typeSpecFlags.get()); 696 } 697 698 return block; 699 } 700 701 // /*package*/ static final int STYLE_NUM_ENTRIES = 6; 702 // /*package*/ static final int STYLE_TYPE = 0; 703 // /*package*/ static final int STYLE_DATA = 1; 704 // /*package*/ static final int STYLE_ASSET_COOKIE = 2; 705 // /*package*/ static final int STYLE_RESOURCE_ID = 3; 706 // 707 // /* Offset within typed data array for native changingConfigurations. */ 708 // static final int STYLE_CHANGING_CONFIGURATIONS = 4; 709 710 // /*package*/ static final int STYLE_DENSITY = 5; 711 712 /* lowercase hexadecimal notation. */ 713 // # define PRIx8 "x" 714 // # define PRIx16 "x" 715 // # define PRIx32 "x" 716 717 @HiddenApi 718 @Implementation(minSdk = O, maxSdk = O_MR1) applyStyle( long themeToken, int defStyleAttr, int defStyleRes, long xmlParserToken, int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress)719 protected static void applyStyle( 720 long themeToken, 721 int defStyleAttr, 722 int defStyleRes, 723 long xmlParserToken, 724 int[] inAttrs, 725 int length, 726 long outValuesAddress, 727 long outIndicesAddress) { 728 ShadowVMRuntime shadowVMRuntime = Shadow.extract(VMRuntime.getRuntime()); 729 int[] outValues = (int[]) shadowVMRuntime.getObjectForAddress(outValuesAddress); 730 int[] outIndices = (int[]) shadowVMRuntime.getObjectForAddress(outIndicesAddress); 731 applyStyle( 732 themeToken, defStyleAttr, defStyleRes, xmlParserToken, inAttrs, outValues, outIndices); 733 } 734 735 @HiddenApi 736 @Implementation(maxSdk = N_MR1) applyStyle( long themeToken, int defStyleAttr, int defStyleRes, long xmlParserToken, int[] attrs, int[] outValues, int[] outIndices)737 protected static boolean applyStyle( 738 long themeToken, 739 int defStyleAttr, 740 int defStyleRes, 741 long xmlParserToken, 742 int[] attrs, 743 int[] outValues, 744 int[] outIndices) { 745 ResTableTheme theme = Registries.NATIVE_THEME_REGISTRY.getNativeObject(themeToken); 746 ResXMLParser xmlParser = 747 xmlParserToken == 0 748 ? null 749 : Registries.NATIVE_RES_XML_PARSERS.getNativeObject(xmlParserToken); 750 AttributeResolution.ApplyStyle( 751 theme, xmlParser, defStyleAttr, defStyleRes, attrs, attrs.length, outValues, outIndices); 752 return true; 753 } 754 755 @Implementation 756 @HiddenApi resolveAttrs( long themeToken, int defStyleAttr, int defStyleRes, int[] inValues, int[] attrs, int[] outValues, int[] outIndices)757 protected static boolean resolveAttrs( 758 long themeToken, 759 int defStyleAttr, 760 int defStyleRes, 761 int[] inValues, 762 int[] attrs, 763 int[] outValues, 764 int[] outIndices) { 765 if (themeToken == 0) { 766 throw new NullPointerException("theme token"); 767 } 768 if (attrs == null) { 769 throw new NullPointerException("attrs"); 770 } 771 if (outValues == null) { 772 throw new NullPointerException("out values"); 773 } 774 775 final int NI = attrs.length; 776 final int NV = outValues.length; 777 if (NV < (NI * STYLE_NUM_ENTRIES)) { 778 throw new IndexOutOfBoundsException("out values too small"); 779 } 780 781 int[] src = attrs; 782 // if (src == null) { 783 // return JNI_FALSE; 784 // } 785 786 int[] srcValues = inValues; 787 final int NSV = srcValues == null ? 0 : inValues.length; 788 789 int[] baseDest = outValues; 790 int destOffset = 0; 791 if (baseDest == null) { 792 return false; 793 } 794 795 int[] indices = null; 796 if (outIndices != null) { 797 if (outIndices.length > NI) { 798 indices = outIndices; 799 } 800 } 801 802 ResTableTheme theme = Registries.NATIVE_THEME_REGISTRY.getNativeObject(themeToken); 803 804 boolean result = 805 AttributeResolution.ResolveAttrs( 806 theme, defStyleAttr, defStyleRes, srcValues, NSV, src, NI, baseDest, indices); 807 808 if (indices != null) { 809 // env.ReleasePrimitiveArrayCritical(outIndices, indices, 0); 810 } 811 // env.ReleasePrimitiveArrayCritical(outValues, baseDest, 0); 812 // env.ReleasePrimitiveArrayCritical(inValues, srcValues, 0); 813 // env.ReleasePrimitiveArrayCritical(attrs, src, 0); 814 815 return result; 816 } 817 818 @HiddenApi 819 @Implementation retrieveAttributes( long xmlParserToken, int[] attrs, int[] outValues, int[] outIndices)820 protected boolean retrieveAttributes( 821 long xmlParserToken, int[] attrs, int[] outValues, int[] outIndices) { 822 if (xmlParserToken == 0) { 823 throw new NullPointerException("xmlParserToken"); 824 // return JNI_FALSE; 825 } 826 if (attrs == null) { 827 throw new NullPointerException("attrs"); 828 // return JNI_FALSE; 829 } 830 if (outValues == null) { 831 throw new NullPointerException("out values"); 832 // return JNI_FALSE; 833 } 834 835 CppAssetManager am = assetManagerForJavaObject(); 836 // if (am == null) { 837 // return JNI_FALSE; 838 // } 839 ResTable res = am.getResources(); 840 // ResXMLParser xmlParser = (ResXMLParser*)xmlParserToken; 841 ResXMLParser xmlParser = Registries.NATIVE_RES_XML_PARSERS.getNativeObject(xmlParserToken); 842 843 // const int NI = env.GetArrayLength(attrs); 844 // const int NV = env.GetArrayLength(outValues); 845 final int NI = attrs.length; 846 final int NV = outValues.length; 847 if (NV < (NI * STYLE_NUM_ENTRIES)) { 848 throw new IndexOutOfBoundsException("out values too small"); 849 // return JNI_FALSE; 850 } 851 852 // int[] src = (int[])env.GetPrimitiveArrayCritical(attrs, 0); 853 // if (src == null) { 854 // return JNI_FALSE; 855 // } 856 int[] src = attrs; 857 858 // int[] baseDest = (int[])env.GetPrimitiveArrayCritical(outValues, 0); 859 int[] baseDest = outValues; 860 if (baseDest == null) { 861 // env.ReleasePrimitiveArrayCritical(attrs, src, 0); 862 // return JNI_FALSE; 863 return false; 864 } 865 866 int[] indices = null; 867 if (outIndices != null) { 868 if (outIndices.length > NI) { 869 // indices = (int[])env.GetPrimitiveArrayCritical(outIndices, 0); 870 indices = outIndices; 871 } 872 } 873 boolean result = 874 AttributeResolution.RetrieveAttributes(res, xmlParser, src, NI, baseDest, indices); 875 876 if (indices != null) { 877 // indices[0] = indicesIdx; 878 // env.ReleasePrimitiveArrayCritical(outIndices, indices, 0); 879 } 880 881 // env.ReleasePrimitiveArrayCritical(outValues, baseDest, 0); 882 // env.ReleasePrimitiveArrayCritical(attrs, src, 0); 883 884 return result; 885 } 886 887 @HiddenApi 888 @Implementation getArraySize(int id)889 protected int getArraySize(int id) { 890 CppAssetManager am = assetManagerForJavaObject(); 891 if (am == null) { 892 return 0; 893 } 894 final ResTable res = am.getResources(); 895 896 res.lock(); 897 final Ref<bag_entry[]> defStyleEnt = new Ref<>(null); 898 int bagOff = res.getBagLocked(id, defStyleEnt, null); 899 res.unlock(); 900 901 return bagOff; 902 } 903 904 @Implementation 905 @HiddenApi retrieveArray(int id, int[] outValues)906 protected int retrieveArray(int id, int[] outValues) { 907 if (outValues == null) { 908 throw new NullPointerException("out values"); 909 } 910 911 CppAssetManager am = assetManagerForJavaObject(); 912 if (am == null) { 913 return 0 /*JNI_FALSE */; 914 } 915 ResTable res = am.getResources(); 916 final Ref<ResTable_config> config = new Ref<>(new ResTable_config()); 917 Res_value value; 918 int block; 919 920 int NV = outValues.length; 921 922 // int[] baseDest = (int[])env->GetPrimitiveArrayCritical(outValues, 0); 923 int[] baseDest = outValues; 924 int[] dest = baseDest; 925 // if (dest == null) { 926 // throw new NullPointerException(env, "java/lang/OutOfMemoryError", ""); 927 // return JNI_FALSE; 928 // } 929 930 // Now lock down the resource object and start pulling stuff from it. 931 res.lock(); 932 933 final Ref<bag_entry[]> arrayEnt = new Ref<>(null); 934 final Ref<Integer> arrayTypeSetFlags = new Ref<>(0); 935 int bagOff = res.getBagLocked(id, arrayEnt, arrayTypeSetFlags); 936 // final ResTable::bag_entry* endArrayEnt = arrayEnt + 937 // (bagOff >= 0 ? bagOff : 0); 938 939 int destOffset = 0; 940 final Ref<Integer> typeSetFlags = new Ref<>(0); 941 while (destOffset < NV 942 && destOffset < bagOff * STYLE_NUM_ENTRIES /*&& arrayEnt < endArrayEnt*/) { 943 bag_entry curArrayEnt = arrayEnt.get()[destOffset / STYLE_NUM_ENTRIES]; 944 945 block = curArrayEnt.stringBlock; 946 typeSetFlags.set(arrayTypeSetFlags.get()); 947 config.get().density = 0; 948 value = curArrayEnt.map.value; 949 950 final Ref<Integer> resid = new Ref<>(0); 951 if (value.dataType != DataType.NULL.code()) { 952 // Take care of resolving the found resource to its final value. 953 // printf("Resolving attribute reference\n"); 954 final Ref<Res_value> resValueRef = new Ref<>(value); 955 int newBlock = res.resolveReference(resValueRef, block, resid, typeSetFlags, config); 956 value = resValueRef.get(); 957 if (kThrowOnBadId) { 958 if (newBlock == BAD_INDEX) { 959 throw new IllegalStateException("Bad resource!"); 960 } 961 } 962 if (newBlock >= 0) block = newBlock; 963 } 964 965 // Deal with the special @null value -- it turns back to TYPE_NULL. 966 if (value.dataType == DataType.REFERENCE.code() && value.data == 0) { 967 value = Res_value.NULL_VALUE; 968 } 969 970 // printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, 971 // value.data); 972 973 // Write the final value back to Java. 974 dest[destOffset + STYLE_TYPE] = value.dataType; 975 dest[destOffset + STYLE_DATA] = value.data; 976 dest[destOffset + STYLE_ASSET_COOKIE] = res.getTableCookie(block); 977 dest[destOffset + STYLE_RESOURCE_ID] = resid.get(); 978 dest[destOffset + STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags.get(); 979 dest[destOffset + STYLE_DENSITY] = config.get().density; 980 // dest += STYLE_NUM_ENTRIES; 981 destOffset += STYLE_NUM_ENTRIES; 982 // arrayEnt++; 983 } 984 985 destOffset /= STYLE_NUM_ENTRIES; 986 987 res.unlock(); 988 989 // env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); 990 991 return destOffset; 992 } 993 994 @HiddenApi 995 @Implementation getNativeStringBlock(int block)996 protected long getNativeStringBlock(int block) { 997 CppAssetManager am = assetManagerForJavaObject(); 998 if (am == null) { 999 return 0; 1000 } 1001 1002 return am.getResources().getTableStringBlock(block).getNativePtr(); 1003 } 1004 1005 @Implementation getAssignedPackageIdentifiers()1006 protected SparseArray<String> getAssignedPackageIdentifiers() { 1007 CppAssetManager am = assetManagerForJavaObject(); 1008 final ResTable res = am.getResources(); 1009 1010 SparseArray<String> sparseArray = new SparseArray<>(); 1011 final int N = res.getBasePackageCount(); 1012 for (int i = 0; i < N; i++) { 1013 final String name = res.getBasePackageName(i); 1014 sparseArray.put(res.getBasePackageId(i), name); 1015 } 1016 return sparseArray; 1017 } 1018 1019 @HiddenApi 1020 @Implementation newTheme()1021 protected long newTheme() { 1022 CppAssetManager am = assetManagerForJavaObject(); 1023 if (am == null) { 1024 return 0; 1025 } 1026 ResTableTheme theme = new ResTableTheme(am.getResources()); 1027 return Registries.NATIVE_THEME_REGISTRY.register(theme); 1028 } 1029 1030 @HiddenApi 1031 @Implementation deleteTheme(long theme)1032 protected void deleteTheme(long theme) { 1033 Registries.NATIVE_THEME_REGISTRY.unregister(theme); 1034 } 1035 1036 @HiddenApi 1037 @Implementation(maxSdk = O_MR1) applyThemeStyle(long themePtr, int styleRes, boolean force)1038 public static void applyThemeStyle(long themePtr, int styleRes, boolean force) { 1039 Registries.NATIVE_THEME_REGISTRY.getNativeObject(themePtr).applyStyle(styleRes, force); 1040 } 1041 1042 @HiddenApi 1043 @Implementation(maxSdk = O_MR1) copyTheme(long destPtr, long sourcePtr)1044 public static void copyTheme(long destPtr, long sourcePtr) { 1045 ResTableTheme dest = Registries.NATIVE_THEME_REGISTRY.getNativeObject(destPtr); 1046 ResTableTheme src = Registries.NATIVE_THEME_REGISTRY.getNativeObject(sourcePtr); 1047 dest.setTo(src); 1048 } 1049 1050 @HiddenApi 1051 @Implementation loadThemeAttributeValue( long themeHandle, int ident, TypedValue outValue, boolean resolve)1052 protected static int loadThemeAttributeValue( 1053 long themeHandle, int ident, TypedValue outValue, boolean resolve) { 1054 ResTableTheme theme = 1055 Preconditions.checkNotNull(Registries.NATIVE_THEME_REGISTRY.getNativeObject(themeHandle)); 1056 ResTable res = theme.getResTable(); 1057 1058 final Ref<Res_value> value = new Ref<>(null); 1059 // XXX value could be different in different configs! 1060 final Ref<Integer> typeSpecFlags = new Ref<>(0); 1061 int block = theme.GetAttribute(ident, value, typeSpecFlags); 1062 final Ref<Integer> ref = new Ref<>(0); 1063 if (resolve) { 1064 block = res.resolveReference(value, block, ref, typeSpecFlags); 1065 if (kThrowOnBadId) { 1066 if (block == BAD_INDEX) { 1067 throw new IllegalStateException("Bad resource!"); 1068 } 1069 } 1070 } 1071 return block >= 0 1072 ? copyValue(outValue, res, value.get(), ref.get(), block, typeSpecFlags.get(), null) 1073 : block; 1074 } 1075 1076 // /*package*/@HiddenApi @Implementation public static final @NativeConfig 1077 // int getThemeChangingConfigurations(long theme); 1078 1079 @HiddenApi 1080 @Implementation openXmlAssetNative(int cookie, String fileName)1081 protected long openXmlAssetNative(int cookie, String fileName) throws FileNotFoundException { 1082 CppAssetManager am = assetManagerForJavaObject(); 1083 if (am == null) { 1084 return 0; 1085 } 1086 1087 ALOGV("openXmlAsset in %s (Java object %s)\n", am, ShadowArscAssetManager.class); 1088 1089 String fileName8 = fileName; 1090 if (fileName8 == null) { 1091 return 0; 1092 } 1093 1094 int assetCookie = cookie; 1095 Asset a; 1096 if (isTruthy(assetCookie)) { 1097 a = am.openNonAsset(assetCookie, fileName8, AccessMode.ACCESS_BUFFER); 1098 } else { 1099 final Ref<Integer> assetCookieRef = new Ref<>(assetCookie); 1100 a = am.openNonAsset(fileName8, AccessMode.ACCESS_BUFFER, assetCookieRef); 1101 assetCookie = assetCookieRef.get(); 1102 } 1103 1104 if (a == null) { 1105 throw new FileNotFoundException(fileName8); 1106 } 1107 1108 final DynamicRefTable dynamicRefTable = 1109 am.getResources().getDynamicRefTableForCookie(assetCookie); 1110 ResXMLTree block = new ResXMLTree(dynamicRefTable); 1111 int err = block.setTo(a.getBuffer(true), (int) a.getLength(), true); 1112 a.close(); 1113 // delete a; 1114 1115 if (err != NO_ERROR) { 1116 throw new FileNotFoundException("Corrupt XML binary file"); 1117 } 1118 1119 return Registries.NATIVE_RES_XML_TREES.register(block); 1120 } 1121 1122 @HiddenApi 1123 @Implementation getArrayStringResource(int arrayResId)1124 protected String[] getArrayStringResource(int arrayResId) { 1125 CppAssetManager am = assetManagerForJavaObject(); 1126 if (am == null) { 1127 return null; 1128 } 1129 final ResTable res = am.getResources(); 1130 1131 final Ref<bag_entry[]> startOfBag = new Ref<>(null); 1132 final int N = res.lockBag(arrayResId, startOfBag); 1133 if (N < 0) { 1134 return null; 1135 } 1136 1137 String[] array = new String[N]; 1138 1139 final Ref<Res_value> valueRef = new Ref<>(null); 1140 final bag_entry[] bag = startOfBag.get(); 1141 int strLen = 0; 1142 for (int i = 0; ((int) i) < N; i++) { 1143 valueRef.set(bag[i].map.value); 1144 String str = null; 1145 1146 // Take care of resolving the found resource to its final value. 1147 int block = res.resolveReference(valueRef, bag[i].stringBlock, null); 1148 if (kThrowOnBadId) { 1149 if (block == BAD_INDEX) { 1150 throw new IllegalStateException("Bad resource!"); 1151 } 1152 } 1153 if (valueRef.get().dataType == DataType.STRING.code()) { 1154 final ResStringPool pool = res.getTableStringBlock(block); 1155 str = pool.stringAt(valueRef.get().data); 1156 1157 // assume we can skip utf8 vs utf 16 handling 1158 1159 // final char* str8 = pool.string8At(value.data, &strLen); 1160 // if (str8 != NULL) { 1161 // str = env.NewStringUTF(str8); 1162 // } else { 1163 // final char16_t* str16 = pool.stringAt(value.data, &strLen); 1164 // str = env.NewString(reinterpret_cast<final jchar*>(str16), 1165 // strLen); 1166 // } 1167 // 1168 // // If one of our NewString{UTF} calls failed due to memory, an 1169 // // exception will be pending. 1170 // if (env.ExceptionCheck()) { 1171 // res.unlockBag(startOfBag); 1172 // return NULL; 1173 // } 1174 if (str == null) { 1175 res.unlockBag(startOfBag); 1176 return null; 1177 } 1178 1179 array[i] = str; 1180 1181 // str is not NULL at that point, otherwise ExceptionCheck would have been true. 1182 // If we have a large amount of strings in our array, we might 1183 // overflow the local reference table of the VM. 1184 // env.DeleteLocalRef(str); 1185 } 1186 } 1187 res.unlockBag(startOfBag); 1188 return array; 1189 } 1190 1191 @HiddenApi 1192 @Implementation getArrayStringInfo(int arrayResId)1193 protected int[] getArrayStringInfo(int arrayResId) { 1194 CppAssetManager am = assetManagerForJavaObject(); 1195 ResTable res = am.getResources(); 1196 1197 final Ref<bag_entry[]> startOfBag = new Ref<>(null); 1198 final int N = res.lockBag(arrayResId, startOfBag); 1199 if (N < 0) { 1200 return null; 1201 } 1202 1203 int[] array = new int[N * 2]; 1204 1205 final Ref<Res_value> value = new Ref<>(null); 1206 bag_entry[] bag = startOfBag.get(); 1207 for (int i = 0, j = 0; i < N; i++) { 1208 int stringIndex = -1; 1209 int stringBlock = 0; 1210 value.set(bag[i].map.value); 1211 1212 // Take care of resolving the found resource to its final value. 1213 stringBlock = res.resolveReference(value, bag[i].stringBlock, null); 1214 if (value.get().dataType == DataType.STRING.code()) { 1215 stringIndex = value.get().data; 1216 } 1217 1218 if (kThrowOnBadId) { 1219 if (stringBlock == BAD_INDEX) { 1220 throw new IllegalStateException("Bad resource!"); 1221 } 1222 } 1223 1224 // todo: It might be faster to allocate a C array to contain 1225 // the blocknums and indices, put them in there and then 1226 // do just one SetIntArrayRegion() 1227 // env->SetIntArrayRegion(array, j, 1, &stringBlock); 1228 array[j] = stringBlock; 1229 // env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); 1230 array[j + 1] = stringIndex; 1231 j += 2; 1232 } 1233 res.unlockBag(startOfBag); 1234 return array; 1235 } 1236 1237 @HiddenApi 1238 @Implementation getArrayIntResource(int arrayResId)1239 public int[] getArrayIntResource(int arrayResId) { 1240 CppAssetManager am = assetManagerForJavaObject(); 1241 if (am == null) { 1242 return null; 1243 } 1244 final ResTable res = am.getResources(); 1245 1246 // final ResTable::bag_entry* startOfBag; 1247 final Ref<bag_entry[]> startOfBag = new Ref<>(null); 1248 final int N = res.lockBag(arrayResId, startOfBag); 1249 if (N < 0) { 1250 return null; 1251 } 1252 1253 int[] array = new int[N]; 1254 if (array == null) { 1255 res.unlockBag(startOfBag); 1256 return null; 1257 } 1258 1259 final Ref<Res_value> valueRef = new Ref<>(null); 1260 bag_entry[] bag = startOfBag.get(); 1261 for (int i = 0; i < N; i++) { 1262 valueRef.set(bag[i].map.value); 1263 1264 // Take care of resolving the found resource to its final value. 1265 int block = res.resolveReference(valueRef, bag[i].stringBlock, null, null, null); 1266 if (kThrowOnBadId) { 1267 if (block == BAD_INDEX) { 1268 res.unlockBag( 1269 startOfBag); // seems like this is missing from android_util_AssetManager.cpp? 1270 throw new IllegalStateException("Bad resource!"); 1271 // return array; 1272 } 1273 } 1274 Res_value value = valueRef.get(); 1275 if (value.dataType >= DataType.TYPE_FIRST_INT && value.dataType <= DataType.TYPE_LAST_INT) { 1276 int intVal = value.data; 1277 // env->SetIntArrayRegion(array, i, 1, &intVal); 1278 array[i] = intVal; 1279 } 1280 } 1281 res.unlockBag(startOfBag); 1282 return array; 1283 } 1284 1285 private static CppAssetManager systemCppAssetManager; 1286 1287 @HiddenApi 1288 @Implementation init(boolean isSystem)1289 protected void init(boolean isSystem) { 1290 // if (isSystem) { 1291 // verifySystemIdmaps(); 1292 // } 1293 1294 Path androidFrameworkJarPath = RuntimeEnvironment.getAndroidFrameworkJarPath(); 1295 Preconditions.checkNotNull(androidFrameworkJarPath); 1296 1297 if (isSystem) { 1298 synchronized (ShadowArscAssetManager.class) { 1299 if (systemCppAssetManager == null) { 1300 systemCppAssetManager = new CppAssetManager(); 1301 systemCppAssetManager.addDefaultAssets(androidFrameworkJarPath); 1302 } 1303 } 1304 this.cppAssetManager = systemCppAssetManager; 1305 } else { 1306 this.cppAssetManager = new CppAssetManager(); 1307 cppAssetManager.addDefaultAssets(androidFrameworkJarPath); 1308 } 1309 1310 ALOGV( 1311 "Created AssetManager %s for Java object %s\n", 1312 cppAssetManager, ShadowArscAssetManager.class); 1313 } 1314 1315 @VisibleForTesting getConfiguration()1316 ResTable_config getConfiguration() { 1317 final Ref<ResTable_config> config = new Ref<>(new ResTable_config()); 1318 assetManagerForJavaObject().getConfiguration(config); 1319 return config.get(); 1320 } 1321 1322 // private native final void destroy(); 1323 1324 // @HiddenApi 1325 // @Implementation 1326 // public int addOverlayPathNative(String idmapPath) { 1327 // if (Strings.isNullOrEmpty(idmapPath)) { 1328 // return 0; 1329 // } 1330 // 1331 // CppAssetManager am = assetManagerForJavaObject(); 1332 // if (am == null) { 1333 // return 0; 1334 // } 1335 // final Ref<Integer> cookie = new Ref<>(null); 1336 // boolean res = am.addOverlayPath(new String8(idmapPath), cookie); 1337 // return (res) ? cookie.get() : 0; 1338 // } 1339 1340 @HiddenApi 1341 @Implementation getStringBlockCount()1342 protected int getStringBlockCount() { 1343 CppAssetManager am = assetManagerForJavaObject(); 1344 if (am == null) { 1345 return 0; 1346 } 1347 return am.getResources().getTableCount(); 1348 } 1349 assetManagerForJavaObject()1350 private synchronized CppAssetManager assetManagerForJavaObject() { 1351 if (cppAssetManager == null) { 1352 throw new NullPointerException(); 1353 } 1354 return cppAssetManager; 1355 } 1356 returnParcelFileDescriptor(Asset a, long[] outOffsets)1357 static ParcelFileDescriptor returnParcelFileDescriptor(Asset a, long[] outOffsets) 1358 throws FileNotFoundException { 1359 final Ref<Long> startOffset = new Ref<Long>(-1L); 1360 final Ref<Long> length = new Ref<Long>(-1L); 1361 ; 1362 FileDescriptor fd = a.openFileDescriptor(startOffset, length); 1363 1364 if (fd == null) { 1365 throw new FileNotFoundException( 1366 "This file can not be opened as a file descriptor; it is probably compressed"); 1367 } 1368 1369 long[] offsets = outOffsets; 1370 if (offsets == null) { 1371 // fd.close(); 1372 return null; 1373 } 1374 1375 offsets[0] = startOffset.get(); 1376 offsets[1] = length.get(); 1377 1378 // FileDescriptor fileDesc = jniCreateFileDescriptor(fd); 1379 // if (fileDesc == null) { 1380 // close(fd); 1381 // return null; 1382 // } 1383 1384 // TODO: consider doing this 1385 // return new ParcelFileDescriptor(fileDesc); 1386 return ParcelFileDescriptor.open(a.getFile(), ParcelFileDescriptor.MODE_READ_ONLY); 1387 } 1388 1389 @Override getAllAssetDirs()1390 Collection<Path> getAllAssetDirs() { 1391 ArrayList<Path> paths = new ArrayList<>(); 1392 for (AssetPath assetPath : cppAssetManager.getAssetPaths()) { 1393 if (Files.isRegularFile(assetPath.file)) { 1394 paths.add(Fs.forJar(assetPath.file).getPath("assets")); 1395 } else { 1396 paths.add(assetPath.file); 1397 } 1398 } 1399 return paths; 1400 } 1401 1402 @VisibleForTesting 1403 @Override getNativePtr()1404 long getNativePtr() { 1405 return reflector(_AssetManager_.class, realObject).getNativePtr(); 1406 } 1407 1408 @Override getAssetPaths()1409 List<AssetPath> getAssetPaths() { 1410 return assetManagerForJavaObject().getAssetPaths(); 1411 } 1412 } 1413