1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.content.res; 18 19 import android.annotation.AnyRes; 20 import android.annotation.ArrayRes; 21 import android.annotation.AttrRes; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.StringRes; 25 import android.annotation.StyleRes; 26 import android.annotation.TestApi; 27 import android.compat.annotation.UnsupportedAppUsage; 28 import android.content.pm.ActivityInfo; 29 import android.content.res.Configuration.NativeConfig; 30 import android.content.res.loader.ResourcesLoader; 31 import android.os.ParcelFileDescriptor; 32 import android.util.ArraySet; 33 import android.util.Log; 34 import android.util.SparseArray; 35 import android.util.TypedValue; 36 37 import com.android.internal.annotations.GuardedBy; 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.internal.content.om.OverlayConfig; 40 41 import java.io.FileDescriptor; 42 import java.io.FileNotFoundException; 43 import java.io.IOException; 44 import java.io.InputStream; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.Collections; 48 import java.util.HashMap; 49 import java.util.List; 50 import java.util.Locale; 51 import java.util.Map; 52 import java.util.Objects; 53 54 /** 55 * Provides access to an application's raw asset files; see {@link Resources} 56 * for the way most applications will want to retrieve their resource data. 57 * This class presents a lower-level API that allows you to open and read raw 58 * files that have been bundled with the application as a simple stream of 59 * bytes. 60 */ 61 public final class AssetManager implements AutoCloseable { 62 private static final String TAG = "AssetManager"; 63 private static final boolean DEBUG_REFS = false; 64 65 private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; 66 67 private static final Object sSync = new Object(); 68 69 private static final ApkAssets[] sEmptyApkAssets = new ApkAssets[0]; 70 71 // Not private for LayoutLib's BridgeAssetManager. 72 @UnsupportedAppUsage 73 @GuardedBy("sSync") static AssetManager sSystem = null; 74 75 @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0]; 76 @GuardedBy("sSync") private static ArraySet<ApkAssets> sSystemApkAssetsSet; 77 78 /** 79 * Mode for {@link #open(String, int)}: no specific information about how 80 * data will be accessed. 81 */ 82 public static final int ACCESS_UNKNOWN = 0; 83 /** 84 * Mode for {@link #open(String, int)}: Read chunks, and seek forward and 85 * backward. 86 */ 87 public static final int ACCESS_RANDOM = 1; 88 /** 89 * Mode for {@link #open(String, int)}: Read sequentially, with an 90 * occasional forward seek. 91 */ 92 public static final int ACCESS_STREAMING = 2; 93 /** 94 * Mode for {@link #open(String, int)}: Attempt to load contents into 95 * memory, for fast small reads. 96 */ 97 public static final int ACCESS_BUFFER = 3; 98 99 @GuardedBy("this") private final TypedValue mValue = new TypedValue(); 100 @GuardedBy("this") private final long[] mOffsets = new long[2]; 101 102 // Pointer to native implementation, stuffed inside a long. 103 @UnsupportedAppUsage 104 @GuardedBy("this") private long mObject; 105 106 // The loaded asset paths. 107 @GuardedBy("this") private ApkAssets[] mApkAssets; 108 109 // Debug/reference counting implementation. 110 @GuardedBy("this") private boolean mOpen = true; 111 @GuardedBy("this") private int mNumRefs = 1; 112 @GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks; 113 114 private ResourcesLoader[] mLoaders; 115 116 /** 117 * A Builder class that helps create an AssetManager with only a single invocation of 118 * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. Without using this builder, 119 * AssetManager must ensure there are system ApkAssets loaded at all times, which when combined 120 * with the user's call to add additional ApkAssets, results in multiple calls to 121 * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. 122 * @hide 123 */ 124 public static class Builder { 125 private ArrayList<ApkAssets> mUserApkAssets = new ArrayList<>(); 126 private ArrayList<ResourcesLoader> mLoaders = new ArrayList<>(); 127 addApkAssets(ApkAssets apkAssets)128 public Builder addApkAssets(ApkAssets apkAssets) { 129 mUserApkAssets.add(apkAssets); 130 return this; 131 } 132 addLoader(ResourcesLoader loader)133 public Builder addLoader(ResourcesLoader loader) { 134 mLoaders.add(loader); 135 return this; 136 } 137 build()138 public AssetManager build() { 139 // Retrieving the system ApkAssets forces their creation as well. 140 final ApkAssets[] systemApkAssets = getSystem().getApkAssets(); 141 142 // Filter ApkAssets so that assets provided by multiple loaders are only included once 143 // in the AssetManager assets. The last appearance of the ApkAssets dictates its load 144 // order. 145 final ArrayList<ApkAssets> loaderApkAssets = new ArrayList<>(); 146 final ArraySet<ApkAssets> uniqueLoaderApkAssets = new ArraySet<>(); 147 for (int i = mLoaders.size() - 1; i >= 0; i--) { 148 final List<ApkAssets> currentLoaderApkAssets = mLoaders.get(i).getApkAssets(); 149 for (int j = currentLoaderApkAssets.size() - 1; j >= 0; j--) { 150 final ApkAssets apkAssets = currentLoaderApkAssets.get(j); 151 if (uniqueLoaderApkAssets.add(apkAssets)) { 152 loaderApkAssets.add(0, apkAssets); 153 } 154 } 155 } 156 157 final int totalApkAssetCount = systemApkAssets.length + mUserApkAssets.size() 158 + loaderApkAssets.size(); 159 final ApkAssets[] apkAssets = new ApkAssets[totalApkAssetCount]; 160 161 System.arraycopy(systemApkAssets, 0, apkAssets, 0, systemApkAssets.length); 162 163 // Append user ApkAssets after system ApkAssets. 164 for (int i = 0, n = mUserApkAssets.size(); i < n; i++) { 165 apkAssets[i + systemApkAssets.length] = mUserApkAssets.get(i); 166 } 167 168 // Append ApkAssets provided by loaders to the end. 169 for (int i = 0, n = loaderApkAssets.size(); i < n; i++) { 170 apkAssets[i + systemApkAssets.length + mUserApkAssets.size()] = 171 loaderApkAssets.get(i); 172 } 173 174 // Calling this constructor prevents creation of system ApkAssets, which we took care 175 // of in this Builder. 176 final AssetManager assetManager = new AssetManager(false /*sentinel*/); 177 assetManager.mApkAssets = apkAssets; 178 AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets, 179 false /*invalidateCaches*/); 180 assetManager.mLoaders = mLoaders.isEmpty() ? null 181 : mLoaders.toArray(new ResourcesLoader[0]); 182 183 return assetManager; 184 } 185 } 186 187 /** 188 * Create a new AssetManager containing only the basic system assets. 189 * Applications will not generally use this method, instead retrieving the 190 * appropriate asset manager with {@link Resources#getAssets}. Not for 191 * use by applications. 192 * @hide 193 */ 194 @UnsupportedAppUsage AssetManager()195 public AssetManager() { 196 final ApkAssets[] assets; 197 synchronized (sSync) { 198 createSystemAssetsInZygoteLocked(false, FRAMEWORK_APK_PATH); 199 assets = sSystemApkAssets; 200 } 201 202 mObject = nativeCreate(); 203 if (DEBUG_REFS) { 204 mNumRefs = 0; 205 incRefsLocked(hashCode()); 206 } 207 208 // Always set the framework resources. 209 setApkAssets(assets, false /*invalidateCaches*/); 210 } 211 212 /** 213 * Private constructor that doesn't call ensureSystemAssets. 214 * Used for the creation of system assets. 215 */ 216 @SuppressWarnings("unused") AssetManager(boolean sentinel)217 private AssetManager(boolean sentinel) { 218 mObject = nativeCreate(); 219 if (DEBUG_REFS) { 220 mNumRefs = 0; 221 incRefsLocked(hashCode()); 222 } 223 } 224 225 /** 226 * This must be called from Zygote so that system assets are shared by all applications. 227 * @hide 228 */ 229 @GuardedBy("sSync") 230 @VisibleForTesting createSystemAssetsInZygoteLocked(boolean reinitialize, String frameworkPath)231 public static void createSystemAssetsInZygoteLocked(boolean reinitialize, 232 String frameworkPath) { 233 if (sSystem != null && !reinitialize) { 234 return; 235 } 236 237 try { 238 final ArrayList<ApkAssets> apkAssets = new ArrayList<>(); 239 apkAssets.add(ApkAssets.loadFromPath(frameworkPath, ApkAssets.PROPERTY_SYSTEM)); 240 241 final String[] systemIdmapPaths = 242 OverlayConfig.getZygoteInstance().createImmutableFrameworkIdmapsInZygote(); 243 for (String idmapPath : systemIdmapPaths) { 244 apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, ApkAssets.PROPERTY_SYSTEM)); 245 } 246 247 sSystemApkAssetsSet = new ArraySet<>(apkAssets); 248 sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); 249 if (sSystem == null) { 250 sSystem = new AssetManager(true /*sentinel*/); 251 } 252 sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/); 253 } catch (IOException e) { 254 throw new IllegalStateException("Failed to create system AssetManager", e); 255 } 256 } 257 258 /** 259 * Return a global shared asset manager that provides access to only 260 * system assets (no application assets). 261 * @hide 262 */ 263 @UnsupportedAppUsage getSystem()264 public static AssetManager getSystem() { 265 synchronized (sSync) { 266 createSystemAssetsInZygoteLocked(false, FRAMEWORK_APK_PATH); 267 return sSystem; 268 } 269 } 270 271 /** 272 * Close this asset manager. 273 */ 274 @Override close()275 public void close() { 276 synchronized (this) { 277 if (!mOpen) { 278 return; 279 } 280 281 mOpen = false; 282 decRefsLocked(hashCode()); 283 } 284 } 285 286 /** 287 * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} 288 * family of methods. 289 * 290 * @param apkAssets The new set of paths. 291 * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. 292 * Set this to false if you are appending new resources 293 * (not new configurations). 294 * @hide 295 */ setApkAssets(@onNull ApkAssets[] apkAssets, boolean invalidateCaches)296 public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { 297 Objects.requireNonNull(apkAssets, "apkAssets"); 298 299 ApkAssets[] newApkAssets = new ApkAssets[sSystemApkAssets.length + apkAssets.length]; 300 301 // Copy the system assets first. 302 System.arraycopy(sSystemApkAssets, 0, newApkAssets, 0, sSystemApkAssets.length); 303 304 // Copy the given ApkAssets if they are not already in the system list. 305 int newLength = sSystemApkAssets.length; 306 for (ApkAssets apkAsset : apkAssets) { 307 if (!sSystemApkAssetsSet.contains(apkAsset)) { 308 newApkAssets[newLength++] = apkAsset; 309 } 310 } 311 312 // Truncate if necessary. 313 if (newLength != newApkAssets.length) { 314 newApkAssets = Arrays.copyOf(newApkAssets, newLength); 315 } 316 317 synchronized (this) { 318 ensureOpenLocked(); 319 mApkAssets = newApkAssets; 320 nativeSetApkAssets(mObject, mApkAssets, invalidateCaches); 321 if (invalidateCaches) { 322 // Invalidate all caches. 323 invalidateCachesLocked(-1); 324 } 325 } 326 } 327 328 /** 329 * Changes the {@link ResourcesLoader ResourcesLoaders} used in this AssetManager. 330 * @hide 331 */ setLoaders(@onNull List<ResourcesLoader> newLoaders)332 void setLoaders(@NonNull List<ResourcesLoader> newLoaders) { 333 Objects.requireNonNull(newLoaders, "newLoaders"); 334 335 final ArrayList<ApkAssets> apkAssets = new ArrayList<>(); 336 for (int i = 0; i < mApkAssets.length; i++) { 337 // Filter out the previous loader apk assets. 338 if (!mApkAssets[i].isForLoader()) { 339 apkAssets.add(mApkAssets[i]); 340 } 341 } 342 343 if (!newLoaders.isEmpty()) { 344 // Filter so that assets provided by multiple loaders are only included once 345 // in the final assets list. The last appearance of the ApkAssets dictates its load 346 // order. 347 final int loaderStartIndex = apkAssets.size(); 348 final ArraySet<ApkAssets> uniqueLoaderApkAssets = new ArraySet<>(); 349 for (int i = newLoaders.size() - 1; i >= 0; i--) { 350 final List<ApkAssets> currentLoaderApkAssets = newLoaders.get(i).getApkAssets(); 351 for (int j = currentLoaderApkAssets.size() - 1; j >= 0; j--) { 352 final ApkAssets loaderApkAssets = currentLoaderApkAssets.get(j); 353 if (uniqueLoaderApkAssets.add(loaderApkAssets)) { 354 apkAssets.add(loaderStartIndex, loaderApkAssets); 355 } 356 } 357 } 358 } 359 360 mLoaders = newLoaders.toArray(new ResourcesLoader[0]); 361 setApkAssets(apkAssets.toArray(new ApkAssets[0]), true /* invalidate_caches */); 362 } 363 364 /** 365 * Invalidates the caches in this AssetManager according to the bitmask `diff`. 366 * 367 * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. 368 * @see ActivityInfo.Config 369 */ invalidateCachesLocked(int diff)370 private void invalidateCachesLocked(int diff) { 371 // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. 372 } 373 374 /** 375 * Returns the set of ApkAssets loaded by this AssetManager. If the AssetManager is closed, this 376 * returns a 0-length array. 377 * @hide 378 */ 379 @UnsupportedAppUsage getApkAssets()380 public @NonNull ApkAssets[] getApkAssets() { 381 synchronized (this) { 382 if (mOpen) { 383 return mApkAssets; 384 } 385 } 386 return sEmptyApkAssets; 387 } 388 389 /** @hide */ 390 @TestApi getApkPaths()391 public @NonNull String[] getApkPaths() { 392 synchronized (this) { 393 if (mOpen) { 394 String[] paths = new String[mApkAssets.length]; 395 final int count = mApkAssets.length; 396 for (int i = 0; i < count; i++) { 397 paths[i] = mApkAssets[i].getAssetPath(); 398 } 399 return paths; 400 } 401 } 402 return new String[0]; 403 } 404 405 /** 406 * Returns a cookie for use with the other APIs of AssetManager. 407 * @return 0 if the path was not found, otherwise a positive integer cookie representing 408 * this path in the AssetManager. 409 * @hide 410 */ findCookieForPath(@onNull String path)411 public int findCookieForPath(@NonNull String path) { 412 Objects.requireNonNull(path, "path"); 413 synchronized (this) { 414 ensureValidLocked(); 415 final int count = mApkAssets.length; 416 for (int i = 0; i < count; i++) { 417 if (path.equals(mApkAssets[i].getAssetPath())) { 418 return i + 1; 419 } 420 } 421 } 422 return 0; 423 } 424 425 /** 426 * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} 427 * @hide 428 */ 429 @Deprecated 430 @UnsupportedAppUsage addAssetPath(String path)431 public int addAssetPath(String path) { 432 return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); 433 } 434 435 /** 436 * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} 437 * @hide 438 */ 439 @Deprecated 440 @UnsupportedAppUsage addAssetPathAsSharedLibrary(String path)441 public int addAssetPathAsSharedLibrary(String path) { 442 return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); 443 } 444 445 /** 446 * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} 447 * @hide 448 */ 449 @Deprecated 450 @UnsupportedAppUsage addOverlayPath(String path)451 public int addOverlayPath(String path) { 452 return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); 453 } 454 addAssetPathInternal(String path, boolean overlay, boolean appAsLib)455 private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { 456 Objects.requireNonNull(path, "path"); 457 synchronized (this) { 458 ensureOpenLocked(); 459 final int count = mApkAssets.length; 460 461 // See if we already have it loaded. 462 for (int i = 0; i < count; i++) { 463 if (mApkAssets[i].getAssetPath().equals(path)) { 464 return i + 1; 465 } 466 } 467 468 final ApkAssets assets; 469 try { 470 if (overlay) { 471 // TODO(b/70343104): This hardcoded path will be removed once 472 // addAssetPathInternal is deleted. 473 final String idmapPath = "/data/resource-cache/" 474 + path.substring(1).replace('/', '@') 475 + "@idmap"; 476 assets = ApkAssets.loadOverlayFromPath(idmapPath, 0 /* flags */); 477 } else { 478 assets = ApkAssets.loadFromPath(path, 479 appAsLib ? ApkAssets.PROPERTY_DYNAMIC : 0); 480 } 481 } catch (IOException e) { 482 return 0; 483 } 484 485 mApkAssets = Arrays.copyOf(mApkAssets, count + 1); 486 mApkAssets[count] = assets; 487 nativeSetApkAssets(mObject, mApkAssets, true); 488 invalidateCachesLocked(-1); 489 return count + 1; 490 } 491 } 492 493 /** @hide */ 494 @NonNull getLoaders()495 public List<ResourcesLoader> getLoaders() { 496 return mLoaders == null ? Collections.emptyList() : Arrays.asList(mLoaders); 497 } 498 499 /** 500 * Ensures that the native implementation has not been destroyed. 501 * The AssetManager may have been closed, but references to it still exist 502 * and therefore the native implementation is not destroyed. 503 */ 504 @GuardedBy("this") ensureValidLocked()505 private void ensureValidLocked() { 506 if (mObject == 0) { 507 throw new RuntimeException("AssetManager has been destroyed"); 508 } 509 } 510 511 /** 512 * Ensures that the AssetManager has not been explicitly closed. If this method passes, 513 * then this implies that ensureValidLocked() also passes. 514 */ 515 @GuardedBy("this") ensureOpenLocked()516 private void ensureOpenLocked() { 517 // If mOpen is true, this implies that mObject != 0. 518 if (!mOpen) { 519 throw new RuntimeException("AssetManager has been closed"); 520 } 521 } 522 523 /** 524 * Populates {@code outValue} with the data associated a particular 525 * resource identifier for the current configuration. 526 * 527 * @param resId the resource identifier to load 528 * @param densityDpi the density bucket for which to load the resource 529 * @param outValue the typed value in which to put the data 530 * @param resolveRefs {@code true} to resolve references, {@code false} 531 * to leave them unresolved 532 * @return {@code true} if the data was loaded into {@code outValue}, 533 * {@code false} otherwise 534 */ 535 @UnsupportedAppUsage getResourceValue(@nyRes int resId, int densityDpi, @NonNull TypedValue outValue, boolean resolveRefs)536 boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, 537 boolean resolveRefs) { 538 Objects.requireNonNull(outValue, "outValue"); 539 synchronized (this) { 540 ensureValidLocked(); 541 final int cookie = nativeGetResourceValue( 542 mObject, resId, (short) densityDpi, outValue, resolveRefs); 543 if (cookie <= 0) { 544 return false; 545 } 546 547 // Convert the changing configurations flags populated by native code. 548 outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( 549 outValue.changingConfigurations); 550 551 if (outValue.type == TypedValue.TYPE_STRING) { 552 outValue.string = getPooledStringForCookie(cookie, outValue.data); 553 } 554 return true; 555 } 556 } 557 558 /** 559 * Retrieves the string value associated with a particular resource 560 * identifier for the current configuration. 561 * 562 * @param resId the resource identifier to load 563 * @return the string value, or {@code null} 564 */ 565 @UnsupportedAppUsage getResourceText(@tringRes int resId)566 @Nullable CharSequence getResourceText(@StringRes int resId) { 567 synchronized (this) { 568 final TypedValue outValue = mValue; 569 if (getResourceValue(resId, 0, outValue, true)) { 570 return outValue.coerceToString(); 571 } 572 return null; 573 } 574 } 575 576 /** 577 * Retrieves the string value associated with a particular resource 578 * identifier for the current configuration. 579 * 580 * @param resId the resource identifier to load 581 * @param bagEntryId the index into the bag to load 582 * @return the string value, or {@code null} 583 */ 584 @UnsupportedAppUsage getResourceBagText(@tringRes int resId, int bagEntryId)585 @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { 586 synchronized (this) { 587 ensureValidLocked(); 588 final TypedValue outValue = mValue; 589 final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); 590 if (cookie <= 0) { 591 return null; 592 } 593 594 // Convert the changing configurations flags populated by native code. 595 outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( 596 outValue.changingConfigurations); 597 598 if (outValue.type == TypedValue.TYPE_STRING) { 599 return getPooledStringForCookie(cookie, outValue.data); 600 } 601 return outValue.coerceToString(); 602 } 603 } 604 getResourceArraySize(@rrayRes int resId)605 int getResourceArraySize(@ArrayRes int resId) { 606 synchronized (this) { 607 ensureValidLocked(); 608 return nativeGetResourceArraySize(mObject, resId); 609 } 610 } 611 612 /** 613 * Populates `outData` with array elements of `resId`. `outData` is normally 614 * used with 615 * {@link TypedArray}. 616 * 617 * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} 618 * long, 619 * with the indices of the data representing the type, value, asset cookie, 620 * resource ID, 621 * configuration change mask, and density of the element. 622 * 623 * @param resId The resource ID of an array resource. 624 * @param outData The array to populate with data. 625 * @return The length of the array. 626 * 627 * @see TypedArray#STYLE_TYPE 628 * @see TypedArray#STYLE_DATA 629 * @see TypedArray#STYLE_ASSET_COOKIE 630 * @see TypedArray#STYLE_RESOURCE_ID 631 * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS 632 * @see TypedArray#STYLE_DENSITY 633 */ getResourceArray(@rrayRes int resId, @NonNull int[] outData)634 int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { 635 Objects.requireNonNull(outData, "outData"); 636 synchronized (this) { 637 ensureValidLocked(); 638 return nativeGetResourceArray(mObject, resId, outData); 639 } 640 } 641 642 /** 643 * Retrieves the string array associated with a particular resource 644 * identifier for the current configuration. 645 * 646 * @param resId the resource identifier of the string array 647 * @return the string array, or {@code null} 648 */ getResourceStringArray(@rrayRes int resId)649 @Nullable String[] getResourceStringArray(@ArrayRes int resId) { 650 synchronized (this) { 651 ensureValidLocked(); 652 return nativeGetResourceStringArray(mObject, resId); 653 } 654 } 655 656 /** 657 * Retrieve the text array associated with a particular resource 658 * identifier. 659 * 660 * @param resId the resource id of the string array 661 */ getResourceTextArray(@rrayRes int resId)662 @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { 663 synchronized (this) { 664 ensureValidLocked(); 665 final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); 666 if (rawInfoArray == null) { 667 return null; 668 } 669 670 final int rawInfoArrayLen = rawInfoArray.length; 671 final int infoArrayLen = rawInfoArrayLen / 2; 672 final CharSequence[] retArray = new CharSequence[infoArrayLen]; 673 for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { 674 int cookie = rawInfoArray[i]; 675 int index = rawInfoArray[i + 1]; 676 retArray[j] = (index >= 0 && cookie > 0) 677 ? getPooledStringForCookie(cookie, index) : null; 678 } 679 return retArray; 680 } 681 } 682 getResourceIntArray(@rrayRes int resId)683 @Nullable int[] getResourceIntArray(@ArrayRes int resId) { 684 synchronized (this) { 685 ensureValidLocked(); 686 return nativeGetResourceIntArray(mObject, resId); 687 } 688 } 689 690 /** 691 * Get the attributes for a style resource. These are the <item> 692 * elements in 693 * a <style> resource. 694 * @param resId The resource ID of the style 695 * @return An array of attribute IDs. 696 */ getStyleAttributes(@tyleRes int resId)697 @AttrRes int[] getStyleAttributes(@StyleRes int resId) { 698 synchronized (this) { 699 ensureValidLocked(); 700 return nativeGetStyleAttributes(mObject, resId); 701 } 702 } 703 704 /** 705 * Populates {@code outValue} with the data associated with a particular 706 * resource identifier for the current configuration. Resolves theme 707 * attributes against the specified theme. 708 * 709 * @param theme the native pointer of the theme 710 * @param resId the resource identifier to load 711 * @param outValue the typed value in which to put the data 712 * @param resolveRefs {@code true} to resolve references, {@code false} 713 * to leave them unresolved 714 * @return {@code true} if the data was loaded into {@code outValue}, 715 * {@code false} otherwise 716 */ getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs)717 boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, 718 boolean resolveRefs) { 719 Objects.requireNonNull(outValue, "outValue"); 720 synchronized (this) { 721 ensureValidLocked(); 722 final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, 723 resolveRefs); 724 if (cookie <= 0) { 725 return false; 726 } 727 728 // Convert the changing configurations flags populated by native code. 729 outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( 730 outValue.changingConfigurations); 731 732 if (outValue.type == TypedValue.TYPE_STRING) { 733 outValue.string = getPooledStringForCookie(cookie, outValue.data); 734 } 735 return true; 736 } 737 } 738 dumpTheme(long theme, int priority, String tag, String prefix)739 void dumpTheme(long theme, int priority, String tag, String prefix) { 740 synchronized (this) { 741 ensureValidLocked(); 742 nativeThemeDump(mObject, theme, priority, tag, prefix); 743 } 744 } 745 746 @UnsupportedAppUsage getResourceName(@nyRes int resId)747 @Nullable String getResourceName(@AnyRes int resId) { 748 synchronized (this) { 749 ensureValidLocked(); 750 return nativeGetResourceName(mObject, resId); 751 } 752 } 753 754 @UnsupportedAppUsage getResourcePackageName(@nyRes int resId)755 @Nullable String getResourcePackageName(@AnyRes int resId) { 756 synchronized (this) { 757 ensureValidLocked(); 758 return nativeGetResourcePackageName(mObject, resId); 759 } 760 } 761 762 @UnsupportedAppUsage getResourceTypeName(@nyRes int resId)763 @Nullable String getResourceTypeName(@AnyRes int resId) { 764 synchronized (this) { 765 ensureValidLocked(); 766 return nativeGetResourceTypeName(mObject, resId); 767 } 768 } 769 770 @UnsupportedAppUsage getResourceEntryName(@nyRes int resId)771 @Nullable String getResourceEntryName(@AnyRes int resId) { 772 synchronized (this) { 773 ensureValidLocked(); 774 return nativeGetResourceEntryName(mObject, resId); 775 } 776 } 777 778 @UnsupportedAppUsage getResourceIdentifier(@onNull String name, @Nullable String defType, @Nullable String defPackage)779 @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType, 780 @Nullable String defPackage) { 781 synchronized (this) { 782 ensureValidLocked(); 783 // name is checked in JNI. 784 return nativeGetResourceIdentifier(mObject, name, defType, defPackage); 785 } 786 } 787 788 /** 789 * Enable resource resolution logging to track the steps taken to resolve the last resource 790 * entry retrieved. Stores the configuration and package names for each step. 791 * 792 * Default disabled. 793 * 794 * @param enabled Boolean indicating whether to enable or disable logging. 795 * 796 * @hide 797 */ 798 @TestApi setResourceResolutionLoggingEnabled(boolean enabled)799 public void setResourceResolutionLoggingEnabled(boolean enabled) { 800 synchronized (this) { 801 ensureValidLocked(); 802 nativeSetResourceResolutionLoggingEnabled(mObject, enabled); 803 } 804 } 805 806 /** 807 * Retrieve the last resource resolution path logged. 808 * 809 * @return Formatted string containing last resource ID/name and steps taken to resolve final 810 * entry, including configuration and package names. 811 * 812 * @hide 813 */ 814 @TestApi getLastResourceResolution()815 public @Nullable String getLastResourceResolution() { 816 synchronized (this) { 817 ensureValidLocked(); 818 return nativeGetLastResourceResolution(mObject); 819 } 820 } 821 822 /** 823 * Returns whether the {@code resources.arsc} of any loaded apk assets is allocated in RAM 824 * (not mmapped). 825 * 826 * @hide 827 */ containsAllocatedTable()828 public boolean containsAllocatedTable() { 829 synchronized (this) { 830 ensureValidLocked(); 831 return nativeContainsAllocatedTable(mObject); 832 } 833 } 834 getPooledStringForCookie(int cookie, int id)835 CharSequence getPooledStringForCookie(int cookie, int id) { 836 // Cookies map to ApkAssets starting at 1. 837 return getApkAssets()[cookie - 1].getStringFromPool(id); 838 } 839 840 /** 841 * Open an asset using ACCESS_STREAMING mode. This provides access to 842 * files that have been bundled with an application as assets -- that is, 843 * files placed in to the "assets" directory. 844 * 845 * @param fileName The name of the asset to open. This name can be hierarchical. 846 * 847 * @see #open(String, int) 848 * @see #list 849 */ open(@onNull String fileName)850 public @NonNull InputStream open(@NonNull String fileName) throws IOException { 851 return open(fileName, ACCESS_STREAMING); 852 } 853 854 /** 855 * Open an asset using an explicit access mode, returning an InputStream to 856 * read its contents. This provides access to files that have been bundled 857 * with an application as assets -- that is, files placed in to the 858 * "assets" directory. 859 * 860 * @param fileName The name of the asset to open. This name can be hierarchical. 861 * @param accessMode Desired access mode for retrieving the data. 862 * 863 * @see #ACCESS_UNKNOWN 864 * @see #ACCESS_STREAMING 865 * @see #ACCESS_RANDOM 866 * @see #ACCESS_BUFFER 867 * @see #open(String) 868 * @see #list 869 */ open(@onNull String fileName, int accessMode)870 public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException { 871 Objects.requireNonNull(fileName, "fileName"); 872 synchronized (this) { 873 ensureOpenLocked(); 874 final long asset = nativeOpenAsset(mObject, fileName, accessMode); 875 if (asset == 0) { 876 throw new FileNotFoundException("Asset file: " + fileName); 877 } 878 final AssetInputStream assetInputStream = new AssetInputStream(asset); 879 incRefsLocked(assetInputStream.hashCode()); 880 return assetInputStream; 881 } 882 } 883 884 /** 885 * Open an uncompressed asset by mmapping it and returning an {@link AssetFileDescriptor}. 886 * This provides access to files that have been bundled with an application as assets -- that 887 * is, files placed in to the "assets" directory. 888 * 889 * The asset must be uncompressed, or an exception will be thrown. 890 * 891 * @param fileName The name of the asset to open. This name can be hierarchical. 892 * @return An open AssetFileDescriptor. 893 */ openFd(@onNull String fileName)894 public @NonNull AssetFileDescriptor openFd(@NonNull String fileName) throws IOException { 895 Objects.requireNonNull(fileName, "fileName"); 896 synchronized (this) { 897 ensureOpenLocked(); 898 final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets); 899 if (pfd == null) { 900 throw new FileNotFoundException("Asset file: " + fileName); 901 } 902 return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); 903 } 904 } 905 906 /** 907 * Return a String array of all the assets at the given path. 908 * 909 * @param path A relative path within the assets, i.e., "docs/home.html". 910 * 911 * @return String[] Array of strings, one for each asset. These file 912 * names are relative to 'path'. You can open the file by 913 * concatenating 'path' and a name in the returned string (via 914 * File) and passing that to open(). 915 * 916 * @see #open 917 */ list(@onNull String path)918 public @Nullable String[] list(@NonNull String path) throws IOException { 919 Objects.requireNonNull(path, "path"); 920 synchronized (this) { 921 ensureValidLocked(); 922 return nativeList(mObject, path); 923 } 924 } 925 926 /** 927 * Open a non-asset file as an asset using ACCESS_STREAMING mode. This 928 * provides direct access to all of the files included in an application 929 * package (not only its assets). Applications should not normally use 930 * this. 931 * 932 * @param fileName Name of the asset to retrieve. 933 * 934 * @see #open(String) 935 * @hide 936 */ 937 @UnsupportedAppUsage openNonAsset(@onNull String fileName)938 public @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException { 939 return openNonAsset(0, fileName, ACCESS_STREAMING); 940 } 941 942 /** 943 * Open a non-asset file as an asset using a specific access mode. This 944 * provides direct access to all of the files included in an application 945 * package (not only its assets). Applications should not normally use 946 * this. 947 * 948 * @param fileName Name of the asset to retrieve. 949 * @param accessMode Desired access mode for retrieving the data. 950 * 951 * @see #ACCESS_UNKNOWN 952 * @see #ACCESS_STREAMING 953 * @see #ACCESS_RANDOM 954 * @see #ACCESS_BUFFER 955 * @see #open(String, int) 956 * @hide 957 */ 958 @UnsupportedAppUsage openNonAsset(@onNull String fileName, int accessMode)959 public @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode) 960 throws IOException { 961 return openNonAsset(0, fileName, accessMode); 962 } 963 964 /** 965 * Open a non-asset in a specified package. Not for use by applications. 966 * 967 * @param cookie Identifier of the package to be opened. 968 * @param fileName Name of the asset to retrieve. 969 * @hide 970 */ 971 @UnsupportedAppUsage openNonAsset(int cookie, @NonNull String fileName)972 public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName) 973 throws IOException { 974 return openNonAsset(cookie, fileName, ACCESS_STREAMING); 975 } 976 977 /** 978 * Open a non-asset in a specified package. Not for use by applications. 979 * 980 * @param cookie Identifier of the package to be opened. 981 * @param fileName Name of the asset to retrieve. 982 * @param accessMode Desired access mode for retrieving the data. 983 * @hide 984 */ 985 @UnsupportedAppUsage openNonAsset(int cookie, @NonNull String fileName, int accessMode)986 public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode) 987 throws IOException { 988 Objects.requireNonNull(fileName, "fileName"); 989 synchronized (this) { 990 ensureOpenLocked(); 991 final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode); 992 if (asset == 0) { 993 throw new FileNotFoundException("Asset absolute file: " + fileName); 994 } 995 final AssetInputStream assetInputStream = new AssetInputStream(asset); 996 incRefsLocked(assetInputStream.hashCode()); 997 return assetInputStream; 998 } 999 } 1000 1001 /** 1002 * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}. 1003 * This provides direct access to all of the files included in an application 1004 * package (not only its assets). Applications should not normally use this. 1005 * 1006 * The asset must not be compressed, or an exception will be thrown. 1007 * 1008 * @param fileName Name of the asset to retrieve. 1009 */ openNonAssetFd(@onNull String fileName)1010 public @NonNull AssetFileDescriptor openNonAssetFd(@NonNull String fileName) 1011 throws IOException { 1012 return openNonAssetFd(0, fileName); 1013 } 1014 1015 /** 1016 * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}. 1017 * This provides direct access to all of the files included in an application 1018 * package (not only its assets). Applications should not normally use this. 1019 * 1020 * The asset must not be compressed, or an exception will be thrown. 1021 * 1022 * @param cookie Identifier of the package to be opened. 1023 * @param fileName Name of the asset to retrieve. 1024 */ openNonAssetFd(int cookie, @NonNull String fileName)1025 public @NonNull AssetFileDescriptor openNonAssetFd(int cookie, @NonNull String fileName) 1026 throws IOException { 1027 Objects.requireNonNull(fileName, "fileName"); 1028 synchronized (this) { 1029 ensureOpenLocked(); 1030 final ParcelFileDescriptor pfd = 1031 nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets); 1032 if (pfd == null) { 1033 throw new FileNotFoundException("Asset absolute file: " + fileName); 1034 } 1035 return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); 1036 } 1037 } 1038 1039 /** 1040 * Retrieve a parser for a compiled XML file. 1041 * 1042 * @param fileName The name of the file to retrieve. 1043 */ openXmlResourceParser(@onNull String fileName)1044 public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName) 1045 throws IOException { 1046 return openXmlResourceParser(0, fileName); 1047 } 1048 1049 /** 1050 * Retrieve a parser for a compiled XML file. 1051 * 1052 * @param cookie Identifier of the package to be opened. 1053 * @param fileName The name of the file to retrieve. 1054 */ openXmlResourceParser(int cookie, @NonNull String fileName)1055 public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName) 1056 throws IOException { 1057 try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) { 1058 XmlResourceParser parser = block.newParser(); 1059 // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with 1060 // a valid native pointer, which makes newParser always return non-null. But let's 1061 // be paranoid. 1062 if (parser == null) { 1063 throw new AssertionError("block.newParser() returned a null parser"); 1064 } 1065 return parser; 1066 } 1067 } 1068 1069 /** 1070 * Retrieve a non-asset as a compiled XML file. Not for use by applications. 1071 * 1072 * @param fileName The name of the file to retrieve. 1073 * @hide 1074 */ openXmlBlockAsset(@onNull String fileName)1075 @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException { 1076 return openXmlBlockAsset(0, fileName); 1077 } 1078 1079 /** 1080 * Retrieve a non-asset as a compiled XML file. Not for use by 1081 * applications. 1082 * 1083 * @param cookie Identifier of the package to be opened. 1084 * @param fileName Name of the asset to retrieve. 1085 * @hide 1086 */ openXmlBlockAsset(int cookie, @NonNull String fileName)1087 @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException { 1088 Objects.requireNonNull(fileName, "fileName"); 1089 synchronized (this) { 1090 ensureOpenLocked(); 1091 1092 final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName); 1093 if (xmlBlock == 0) { 1094 throw new FileNotFoundException("Asset XML file: " + fileName); 1095 } 1096 final XmlBlock block = new XmlBlock(this, xmlBlock); 1097 incRefsLocked(block.hashCode()); 1098 return block; 1099 } 1100 } 1101 xmlBlockGone(int id)1102 void xmlBlockGone(int id) { 1103 synchronized (this) { 1104 decRefsLocked(id); 1105 } 1106 } 1107 1108 @UnsupportedAppUsage applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress, long outIndicesAddress)1109 void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, 1110 @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress, 1111 long outIndicesAddress) { 1112 Objects.requireNonNull(inAttrs, "inAttrs"); 1113 synchronized (this) { 1114 // Need to synchronize on AssetManager because we will be accessing 1115 // the native implementation of AssetManager. 1116 ensureValidLocked(); 1117 nativeApplyStyle(mObject, themePtr, defStyleAttr, defStyleRes, 1118 parser != null ? parser.mParseState : 0, inAttrs, outValuesAddress, 1119 outIndicesAddress); 1120 } 1121 } 1122 getAttributeResolutionStack(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @StyleRes int xmlStyle)1123 int[] getAttributeResolutionStack(long themePtr, @AttrRes int defStyleAttr, 1124 @StyleRes int defStyleRes, @StyleRes int xmlStyle) { 1125 synchronized (this) { 1126 return nativeAttributeResolutionStack( 1127 mObject, themePtr, xmlStyle, defStyleAttr, defStyleRes); 1128 } 1129 } 1130 1131 @UnsupportedAppUsage resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices)1132 boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, 1133 @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues, 1134 @NonNull int[] outIndices) { 1135 Objects.requireNonNull(inAttrs, "inAttrs"); 1136 Objects.requireNonNull(outValues, "outValues"); 1137 Objects.requireNonNull(outIndices, "outIndices"); 1138 synchronized (this) { 1139 // Need to synchronize on AssetManager because we will be accessing 1140 // the native implementation of AssetManager. 1141 ensureValidLocked(); 1142 return nativeResolveAttrs(mObject, 1143 themePtr, defStyleAttr, defStyleRes, inValues, inAttrs, outValues, outIndices); 1144 } 1145 } 1146 1147 @UnsupportedAppUsage retrieveAttributes(@onNull XmlBlock.Parser parser, @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices)1148 boolean retrieveAttributes(@NonNull XmlBlock.Parser parser, @NonNull int[] inAttrs, 1149 @NonNull int[] outValues, @NonNull int[] outIndices) { 1150 Objects.requireNonNull(parser, "parser"); 1151 Objects.requireNonNull(inAttrs, "inAttrs"); 1152 Objects.requireNonNull(outValues, "outValues"); 1153 Objects.requireNonNull(outIndices, "outIndices"); 1154 synchronized (this) { 1155 // Need to synchronize on AssetManager because we will be accessing 1156 // the native implementation of AssetManager. 1157 ensureValidLocked(); 1158 return nativeRetrieveAttributes( 1159 mObject, parser.mParseState, inAttrs, outValues, outIndices); 1160 } 1161 } 1162 1163 @UnsupportedAppUsage createTheme()1164 long createTheme() { 1165 synchronized (this) { 1166 ensureValidLocked(); 1167 long themePtr = nativeThemeCreate(mObject); 1168 incRefsLocked(themePtr); 1169 return themePtr; 1170 } 1171 } 1172 releaseTheme(long themePtr)1173 void releaseTheme(long themePtr) { 1174 synchronized (this) { 1175 nativeThemeDestroy(themePtr); 1176 decRefsLocked(themePtr); 1177 } 1178 } 1179 applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force)1180 void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) { 1181 synchronized (this) { 1182 // Need to synchronize on AssetManager because we will be accessing 1183 // the native implementation of AssetManager. 1184 ensureValidLocked(); 1185 nativeThemeApplyStyle(mObject, themePtr, resId, force); 1186 } 1187 } 1188 1189 @UnsupportedAppUsage setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr)1190 void setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr) { 1191 synchronized (this) { 1192 ensureValidLocked(); 1193 synchronized (srcAssetManager) { 1194 srcAssetManager.ensureValidLocked(); 1195 nativeThemeCopy(mObject, dstThemePtr, srcAssetManager.mObject, srcThemePtr); 1196 } 1197 } 1198 } 1199 1200 @Override finalize()1201 protected void finalize() throws Throwable { 1202 if (DEBUG_REFS && mNumRefs != 0) { 1203 Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs); 1204 if (mRefStacks != null) { 1205 for (RuntimeException e : mRefStacks.values()) { 1206 Log.w(TAG, "Reference from here", e); 1207 } 1208 } 1209 } 1210 1211 synchronized (this) { 1212 if (mObject != 0) { 1213 nativeDestroy(mObject); 1214 mObject = 0; 1215 } 1216 } 1217 } 1218 1219 /* No Locking is needed for AssetInputStream because an AssetInputStream is not-thread 1220 safe and it does not rely on AssetManager once it has been created. It completely owns the 1221 underlying Asset. */ 1222 public final class AssetInputStream extends InputStream { 1223 private long mAssetNativePtr; 1224 private long mLength; 1225 private long mMarkPos; 1226 1227 /** 1228 * @hide 1229 */ 1230 @UnsupportedAppUsage getAssetInt()1231 public final int getAssetInt() { 1232 throw new UnsupportedOperationException(); 1233 } 1234 1235 /** 1236 * @hide 1237 */ 1238 @UnsupportedAppUsage getNativeAsset()1239 public final long getNativeAsset() { 1240 return mAssetNativePtr; 1241 } 1242 AssetInputStream(long assetNativePtr)1243 private AssetInputStream(long assetNativePtr) { 1244 mAssetNativePtr = assetNativePtr; 1245 mLength = nativeAssetGetLength(assetNativePtr); 1246 } 1247 1248 @Override read()1249 public final int read() throws IOException { 1250 ensureOpen(); 1251 return nativeAssetReadChar(mAssetNativePtr); 1252 } 1253 1254 @Override read(@onNull byte[] b)1255 public final int read(@NonNull byte[] b) throws IOException { 1256 ensureOpen(); 1257 Objects.requireNonNull(b, "b"); 1258 return nativeAssetRead(mAssetNativePtr, b, 0, b.length); 1259 } 1260 1261 @Override read(@onNull byte[] b, int off, int len)1262 public final int read(@NonNull byte[] b, int off, int len) throws IOException { 1263 ensureOpen(); 1264 Objects.requireNonNull(b, "b"); 1265 return nativeAssetRead(mAssetNativePtr, b, off, len); 1266 } 1267 1268 @Override skip(long n)1269 public final long skip(long n) throws IOException { 1270 ensureOpen(); 1271 long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); 1272 if ((pos + n) > mLength) { 1273 n = mLength - pos; 1274 } 1275 if (n > 0) { 1276 nativeAssetSeek(mAssetNativePtr, n, 0); 1277 } 1278 return n; 1279 } 1280 1281 @Override available()1282 public final int available() throws IOException { 1283 ensureOpen(); 1284 final long len = nativeAssetGetRemainingLength(mAssetNativePtr); 1285 return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; 1286 } 1287 1288 @Override markSupported()1289 public final boolean markSupported() { 1290 return true; 1291 } 1292 1293 @Override mark(int readlimit)1294 public final void mark(int readlimit) { 1295 ensureOpen(); 1296 mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); 1297 } 1298 1299 @Override reset()1300 public final void reset() throws IOException { 1301 ensureOpen(); 1302 nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); 1303 } 1304 1305 @Override close()1306 public final void close() throws IOException { 1307 if (mAssetNativePtr != 0) { 1308 nativeAssetDestroy(mAssetNativePtr); 1309 mAssetNativePtr = 0; 1310 1311 synchronized (AssetManager.this) { 1312 decRefsLocked(hashCode()); 1313 } 1314 } 1315 } 1316 1317 @Override finalize()1318 protected void finalize() throws Throwable { 1319 close(); 1320 } 1321 ensureOpen()1322 private void ensureOpen() { 1323 if (mAssetNativePtr == 0) { 1324 throw new IllegalStateException("AssetInputStream is closed"); 1325 } 1326 } 1327 } 1328 1329 /** 1330 * Determine whether the state in this asset manager is up-to-date with 1331 * the files on the filesystem. If false is returned, you need to 1332 * instantiate a new AssetManager class to see the new data. 1333 * @hide 1334 */ 1335 @UnsupportedAppUsage isUpToDate()1336 public boolean isUpToDate() { 1337 synchronized (this) { 1338 if (!mOpen) { 1339 return false; 1340 } 1341 1342 for (ApkAssets apkAssets : mApkAssets) { 1343 if (!apkAssets.isUpToDate()) { 1344 return false; 1345 } 1346 } 1347 1348 return true; 1349 } 1350 } 1351 1352 /** 1353 * Get the locales that this asset manager contains data for. 1354 * 1355 * <p>On SDK 21 (Android 5.0: Lollipop) and above, Locale strings are valid 1356 * <a href="https://tools.ietf.org/html/bcp47">BCP-47</a> language tags and can be 1357 * parsed using {@link Locale#forLanguageTag(String)}. 1358 * 1359 * <p>On SDK 20 (Android 4.4W: Kitkat for watches) and below, locale strings 1360 * are of the form {@code ll_CC} where {@code ll} is a two letter language code, 1361 * and {@code CC} is a two letter country code. 1362 */ getLocales()1363 public String[] getLocales() { 1364 synchronized (this) { 1365 ensureValidLocked(); 1366 return nativeGetLocales(mObject, false /*excludeSystem*/); 1367 } 1368 } 1369 1370 /** 1371 * Same as getLocales(), except that locales that are only provided by the system (i.e. those 1372 * present in framework-res.apk or its overlays) will not be listed. 1373 * 1374 * For example, if the "system" assets support English, French, and German, and the additional 1375 * assets support Cherokee and French, getLocales() would return 1376 * [Cherokee, English, French, German], while getNonSystemLocales() would return 1377 * [Cherokee, French]. 1378 * @hide 1379 */ getNonSystemLocales()1380 public String[] getNonSystemLocales() { 1381 synchronized (this) { 1382 ensureValidLocked(); 1383 return nativeGetLocales(mObject, true /*excludeSystem*/); 1384 } 1385 } 1386 1387 /** 1388 * @hide 1389 */ getSizeConfigurations()1390 Configuration[] getSizeConfigurations() { 1391 synchronized (this) { 1392 ensureValidLocked(); 1393 return nativeGetSizeConfigurations(mObject); 1394 } 1395 } 1396 1397 /** 1398 * Change the configuration used when retrieving resources. Not for use by 1399 * applications. 1400 * @hide 1401 */ 1402 @UnsupportedAppUsage setConfiguration(int mcc, int mnc, @Nullable 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 majorVersion)1403 public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, 1404 int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, 1405 int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, 1406 int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) { 1407 synchronized (this) { 1408 ensureValidLocked(); 1409 nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, 1410 keyboard, keyboardHidden, navigation, screenWidth, screenHeight, 1411 smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, 1412 colorMode, majorVersion); 1413 } 1414 } 1415 1416 /** 1417 * @hide 1418 */ 1419 @UnsupportedAppUsage getAssignedPackageIdentifiers()1420 public SparseArray<String> getAssignedPackageIdentifiers() { 1421 return getAssignedPackageIdentifiers(true, true); 1422 } 1423 1424 /** 1425 * @hide 1426 */ getAssignedPackageIdentifiers(boolean includeOverlays, boolean includeLoaders)1427 public SparseArray<String> getAssignedPackageIdentifiers(boolean includeOverlays, 1428 boolean includeLoaders) { 1429 synchronized (this) { 1430 ensureValidLocked(); 1431 return nativeGetAssignedPackageIdentifiers(mObject, includeOverlays, includeLoaders); 1432 } 1433 } 1434 1435 /** 1436 * @hide 1437 */ 1438 @GuardedBy("this") getOverlayableMap(String packageName)1439 public @Nullable Map<String, String> getOverlayableMap(String packageName) { 1440 synchronized (this) { 1441 ensureValidLocked(); 1442 return nativeGetOverlayableMap(mObject, packageName); 1443 } 1444 } 1445 1446 /** 1447 * @hide 1448 */ 1449 @TestApi 1450 @GuardedBy("this") getOverlayablesToString(String packageName)1451 public @Nullable String getOverlayablesToString(String packageName) { 1452 synchronized (this) { 1453 ensureValidLocked(); 1454 return nativeGetOverlayablesToString(mObject, packageName); 1455 } 1456 } 1457 1458 @GuardedBy("this") incRefsLocked(long id)1459 private void incRefsLocked(long id) { 1460 if (DEBUG_REFS) { 1461 if (mRefStacks == null) { 1462 mRefStacks = new HashMap<>(); 1463 } 1464 RuntimeException ex = new RuntimeException(); 1465 ex.fillInStackTrace(); 1466 mRefStacks.put(id, ex); 1467 } 1468 mNumRefs++; 1469 } 1470 1471 @GuardedBy("this") decRefsLocked(long id)1472 private void decRefsLocked(long id) { 1473 if (DEBUG_REFS && mRefStacks != null) { 1474 mRefStacks.remove(id); 1475 } 1476 mNumRefs--; 1477 if (mNumRefs == 0 && mObject != 0) { 1478 nativeDestroy(mObject); 1479 mObject = 0; 1480 mApkAssets = sEmptyApkAssets; 1481 } 1482 } 1483 1484 // AssetManager setup native methods. nativeCreate()1485 private static native long nativeCreate(); nativeDestroy(long ptr)1486 private static native void nativeDestroy(long ptr); nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, boolean invalidateCaches)1487 private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, 1488 boolean invalidateCaches); nativeSetConfiguration(long ptr, int mcc, int mnc, @Nullable 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 majorVersion)1489 private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, 1490 @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, 1491 int keyboardHidden, int navigation, int screenWidth, int screenHeight, 1492 int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, 1493 int uiMode, int colorMode, int majorVersion); nativeGetAssignedPackageIdentifiers( long ptr, boolean includeOverlays, boolean includeLoaders)1494 private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers( 1495 long ptr, boolean includeOverlays, boolean includeLoaders); 1496 1497 // File native methods. nativeContainsAllocatedTable(long ptr)1498 private static native boolean nativeContainsAllocatedTable(long ptr); nativeList(long ptr, @NonNull String path)1499 private static native @Nullable String[] nativeList(long ptr, @NonNull String path) 1500 throws IOException; nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode)1501 private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); nativeOpenAssetFd(long ptr, @NonNull String fileName, long[] outOffsets)1502 private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, 1503 @NonNull String fileName, long[] outOffsets) throws IOException; nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, int accessMode)1504 private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, 1505 int accessMode); nativeOpenNonAssetFd(long ptr, int cookie, @NonNull String fileName, @NonNull long[] outOffsets)1506 private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, 1507 @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName)1508 private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); nativeOpenXmlAssetFd(long ptr, int cookie, @NonNull FileDescriptor fileDescriptor)1509 private static native long nativeOpenXmlAssetFd(long ptr, int cookie, 1510 @NonNull FileDescriptor fileDescriptor); 1511 1512 // Primitive resource native methods. nativeGetResourceValue(long ptr, @AnyRes int resId, short density, @NonNull TypedValue outValue, boolean resolveReferences)1513 private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, 1514 @NonNull TypedValue outValue, boolean resolveReferences); nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, @NonNull TypedValue outValue)1515 private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, 1516 @NonNull TypedValue outValue); 1517 nativeGetStyleAttributes(long ptr, @StyleRes int resId)1518 private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, 1519 @StyleRes int resId); nativeGetResourceStringArray(long ptr, @ArrayRes int resId)1520 private static native @Nullable String[] nativeGetResourceStringArray(long ptr, 1521 @ArrayRes int resId); nativeGetResourceStringArrayInfo(long ptr, @ArrayRes int resId)1522 private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, 1523 @ArrayRes int resId); nativeGetResourceIntArray(long ptr, @ArrayRes int resId)1524 private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); nativeGetResourceArraySize(long ptr, @ArrayRes int resId)1525 private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); nativeGetResourceArray(long ptr, @ArrayRes int resId, @NonNull int[] outValues)1526 private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, 1527 @NonNull int[] outValues); 1528 1529 // Resource name/ID native methods. nativeGetResourceIdentifier(long ptr, @NonNull String name, @Nullable String defType, @Nullable String defPackage)1530 private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, 1531 @Nullable String defType, @Nullable String defPackage); nativeGetResourceName(long ptr, @AnyRes int resid)1532 private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); nativeGetResourcePackageName(long ptr, @AnyRes int resid)1533 private static native @Nullable String nativeGetResourcePackageName(long ptr, 1534 @AnyRes int resid); nativeGetResourceTypeName(long ptr, @AnyRes int resid)1535 private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); nativeGetResourceEntryName(long ptr, @AnyRes int resid)1536 private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); nativeGetLocales(long ptr, boolean excludeSystem)1537 private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); nativeGetSizeConfigurations(long ptr)1538 private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled)1539 private static native void nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled); nativeGetLastResourceResolution(long ptr)1540 private static native @Nullable String nativeGetLastResourceResolution(long ptr); 1541 1542 // Style attribute retrieval native methods. nativeAttributeResolutionStack(long ptr, long themePtr, @StyleRes int xmlStyleRes, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)1543 private static native int[] nativeAttributeResolutionStack(long ptr, long themePtr, 1544 @StyleRes int xmlStyleRes, @AttrRes int defStyleAttr, @StyleRes int defStyleRes); nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, long outValuesAddress, long outIndicesAddress)1545 private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, 1546 @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, 1547 long outValuesAddress, long outIndicesAddress); nativeResolveAttrs(long ptr, long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices)1548 private static native boolean nativeResolveAttrs(long ptr, long themePtr, 1549 @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, 1550 @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); nativeRetrieveAttributes(long ptr, long xmlParserPtr, @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices)1551 private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, 1552 @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); 1553 1554 // Theme related native methods nativeThemeCreate(long ptr)1555 private static native long nativeThemeCreate(long ptr); nativeThemeDestroy(long themePtr)1556 private static native void nativeThemeDestroy(long themePtr); nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, boolean force)1557 private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, 1558 boolean force); nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr, long srcAssetManagerPtr, long srcThemePtr)1559 private static native void nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr, 1560 long srcAssetManagerPtr, long srcThemePtr); nativeThemeClear(long themePtr)1561 static native void nativeThemeClear(long themePtr); nativeThemeGetAttributeValue(long ptr, long themePtr, @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve)1562 private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, 1563 @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); nativeThemeDump(long ptr, long themePtr, int priority, String tag, String prefix)1564 private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, 1565 String prefix); nativeThemeGetChangingConfigurations(long themePtr)1566 static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); 1567 1568 // AssetInputStream related native methods. nativeAssetDestroy(long assetPtr)1569 private static native void nativeAssetDestroy(long assetPtr); nativeAssetReadChar(long assetPtr)1570 private static native int nativeAssetReadChar(long assetPtr); nativeAssetRead(long assetPtr, byte[] b, int off, int len)1571 private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); nativeAssetSeek(long assetPtr, long offset, int whence)1572 private static native long nativeAssetSeek(long assetPtr, long offset, int whence); nativeAssetGetLength(long assetPtr)1573 private static native long nativeAssetGetLength(long assetPtr); nativeAssetGetRemainingLength(long assetPtr)1574 private static native long nativeAssetGetRemainingLength(long assetPtr); 1575 nativeCreateIdmapsForStaticOverlaysTargetingAndroid()1576 private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid(); nativeGetOverlayableMap(long ptr, @NonNull String packageName)1577 private static native @Nullable Map nativeGetOverlayableMap(long ptr, 1578 @NonNull String packageName); nativeGetOverlayablesToString(long ptr, @NonNull String packageName)1579 private static native @Nullable String nativeGetOverlayablesToString(long ptr, 1580 @NonNull String packageName); 1581 1582 // Global debug native methods. 1583 /** 1584 * @hide 1585 */ 1586 @UnsupportedAppUsage getGlobalAssetCount()1587 public static native int getGlobalAssetCount(); 1588 1589 /** 1590 * @hide 1591 */ getAssetAllocations()1592 public static native String getAssetAllocations(); 1593 1594 /** 1595 * @hide 1596 */ 1597 @UnsupportedAppUsage getGlobalAssetManagerCount()1598 public static native int getGlobalAssetManagerCount(); 1599 } 1600