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