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