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 // Let's still check if the native object exists, given all the memory corruptions. 532 if (mObject == 0) { 533 throw new RuntimeException("AssetManager is open but the native object is gone"); 534 } 535 } 536 537 /** 538 * Populates {@code outValue} with the data associated a particular 539 * resource identifier for the current configuration. 540 * 541 * @param resId the resource identifier to load 542 * @param densityDpi the density bucket for which to load the resource 543 * @param outValue the typed value in which to put the data 544 * @param resolveRefs {@code true} to resolve references, {@code false} 545 * to leave them unresolved 546 * @return {@code true} if the data was loaded into {@code outValue}, 547 * {@code false} otherwise 548 */ 549 @UnsupportedAppUsage getResourceValue(@nyRes int resId, int densityDpi, @NonNull TypedValue outValue, boolean resolveRefs)550 boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, 551 boolean resolveRefs) { 552 Objects.requireNonNull(outValue, "outValue"); 553 synchronized (this) { 554 ensureValidLocked(); 555 final int cookie = nativeGetResourceValue( 556 mObject, resId, (short) densityDpi, outValue, resolveRefs); 557 if (cookie <= 0) { 558 return false; 559 } 560 561 // Convert the changing configurations flags populated by native code. 562 outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( 563 outValue.changingConfigurations); 564 565 if (outValue.type == TypedValue.TYPE_STRING) { 566 if ((outValue.string = getPooledStringForCookie(cookie, outValue.data)) == null) { 567 return false; 568 } 569 } 570 return true; 571 } 572 } 573 574 /** 575 * Retrieves the string value associated with a particular resource 576 * identifier for the current configuration. 577 * 578 * @param resId the resource identifier to load 579 * @return the string value, or {@code null} 580 */ 581 @UnsupportedAppUsage getResourceText(@tringRes int resId)582 @Nullable CharSequence getResourceText(@StringRes int resId) { 583 synchronized (this) { 584 final TypedValue outValue = mValue; 585 if (getResourceValue(resId, 0, outValue, true)) { 586 return outValue.coerceToString(); 587 } 588 return null; 589 } 590 } 591 592 /** 593 * Retrieves the string value associated with a particular resource 594 * identifier for the current configuration. 595 * 596 * @param resId the resource identifier to load 597 * @param bagEntryId the index into the bag to load 598 * @return the string value, or {@code null} 599 */ 600 @UnsupportedAppUsage getResourceBagText(@tringRes int resId, int bagEntryId)601 @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { 602 synchronized (this) { 603 ensureValidLocked(); 604 final TypedValue outValue = mValue; 605 final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); 606 if (cookie <= 0) { 607 return null; 608 } 609 610 // Convert the changing configurations flags populated by native code. 611 outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( 612 outValue.changingConfigurations); 613 614 if (outValue.type == TypedValue.TYPE_STRING) { 615 return getPooledStringForCookie(cookie, outValue.data); 616 } 617 return outValue.coerceToString(); 618 } 619 } 620 getResourceArraySize(@rrayRes int resId)621 int getResourceArraySize(@ArrayRes int resId) { 622 synchronized (this) { 623 ensureValidLocked(); 624 return nativeGetResourceArraySize(mObject, resId); 625 } 626 } 627 628 /** 629 * Populates `outData` with array elements of `resId`. `outData` is normally 630 * used with 631 * {@link TypedArray}. 632 * 633 * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} 634 * long, 635 * with the indices of the data representing the type, value, asset cookie, 636 * resource ID, 637 * configuration change mask, and density of the element. 638 * 639 * @param resId The resource ID of an array resource. 640 * @param outData The array to populate with data. 641 * @return The length of the array. 642 * 643 * @see TypedArray#STYLE_TYPE 644 * @see TypedArray#STYLE_DATA 645 * @see TypedArray#STYLE_ASSET_COOKIE 646 * @see TypedArray#STYLE_RESOURCE_ID 647 * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS 648 * @see TypedArray#STYLE_DENSITY 649 */ getResourceArray(@rrayRes int resId, @NonNull int[] outData)650 int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { 651 Objects.requireNonNull(outData, "outData"); 652 synchronized (this) { 653 ensureValidLocked(); 654 return nativeGetResourceArray(mObject, resId, outData); 655 } 656 } 657 658 /** 659 * Retrieves the string array associated with a particular resource 660 * identifier for the current configuration. 661 * 662 * @param resId the resource identifier of the string array 663 * @return the string array, or {@code null} 664 */ getResourceStringArray(@rrayRes int resId)665 @Nullable String[] getResourceStringArray(@ArrayRes int resId) { 666 synchronized (this) { 667 ensureValidLocked(); 668 return nativeGetResourceStringArray(mObject, resId); 669 } 670 } 671 672 /** 673 * Retrieve the text array associated with a particular resource 674 * identifier. 675 * 676 * @param resId the resource id of the string array 677 */ getResourceTextArray(@rrayRes int resId)678 @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { 679 synchronized (this) { 680 ensureValidLocked(); 681 final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); 682 if (rawInfoArray == null) { 683 return null; 684 } 685 686 final int rawInfoArrayLen = rawInfoArray.length; 687 final int infoArrayLen = rawInfoArrayLen / 2; 688 final CharSequence[] retArray = new CharSequence[infoArrayLen]; 689 for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { 690 int cookie = rawInfoArray[i]; 691 int index = rawInfoArray[i + 1]; 692 retArray[j] = (index >= 0 && cookie > 0) 693 ? getPooledStringForCookie(cookie, index) : null; 694 } 695 return retArray; 696 } 697 } 698 getResourceIntArray(@rrayRes int resId)699 @Nullable int[] getResourceIntArray(@ArrayRes int resId) { 700 synchronized (this) { 701 ensureValidLocked(); 702 return nativeGetResourceIntArray(mObject, resId); 703 } 704 } 705 706 /** 707 * Get the attributes for a style resource. These are the <item> 708 * elements in 709 * a <style> resource. 710 * @param resId The resource ID of the style 711 * @return An array of attribute IDs. 712 */ getStyleAttributes(@tyleRes int resId)713 @AttrRes int[] getStyleAttributes(@StyleRes int resId) { 714 synchronized (this) { 715 ensureValidLocked(); 716 return nativeGetStyleAttributes(mObject, resId); 717 } 718 } 719 720 /** 721 * Populates {@code outValue} with the data associated with a particular 722 * resource identifier for the current configuration. Resolves theme 723 * attributes against the specified theme. 724 * 725 * @param theme the native pointer of the theme 726 * @param resId the resource identifier to load 727 * @param outValue the typed value in which to put the data 728 * @param resolveRefs {@code true} to resolve references, {@code false} 729 * to leave them unresolved 730 * @return {@code true} if the data was loaded into {@code outValue}, 731 * {@code false} otherwise 732 */ getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs)733 boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, 734 boolean resolveRefs) { 735 Objects.requireNonNull(outValue, "outValue"); 736 synchronized (this) { 737 ensureValidLocked(); 738 final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, 739 resolveRefs); 740 if (cookie <= 0) { 741 return false; 742 } 743 744 // Convert the changing configurations flags populated by native code. 745 outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( 746 outValue.changingConfigurations); 747 748 if (outValue.type == TypedValue.TYPE_STRING) { 749 if ((outValue.string = getPooledStringForCookie(cookie, outValue.data)) == null) { 750 return false; 751 } 752 } 753 return true; 754 } 755 } 756 dumpTheme(long theme, int priority, String tag, String prefix)757 void dumpTheme(long theme, int priority, String tag, String prefix) { 758 synchronized (this) { 759 ensureValidLocked(); 760 nativeThemeDump(mObject, theme, priority, tag, prefix); 761 } 762 } 763 764 @UnsupportedAppUsage getResourceName(@nyRes int resId)765 @Nullable String getResourceName(@AnyRes int resId) { 766 synchronized (this) { 767 ensureValidLocked(); 768 return nativeGetResourceName(mObject, resId); 769 } 770 } 771 772 @UnsupportedAppUsage getResourcePackageName(@nyRes int resId)773 @Nullable String getResourcePackageName(@AnyRes int resId) { 774 synchronized (this) { 775 ensureValidLocked(); 776 return nativeGetResourcePackageName(mObject, resId); 777 } 778 } 779 780 @UnsupportedAppUsage getResourceTypeName(@nyRes int resId)781 @Nullable String getResourceTypeName(@AnyRes int resId) { 782 synchronized (this) { 783 ensureValidLocked(); 784 return nativeGetResourceTypeName(mObject, resId); 785 } 786 } 787 788 @UnsupportedAppUsage getResourceEntryName(@nyRes int resId)789 @Nullable String getResourceEntryName(@AnyRes int resId) { 790 synchronized (this) { 791 ensureValidLocked(); 792 return nativeGetResourceEntryName(mObject, resId); 793 } 794 } 795 796 @UnsupportedAppUsage getResourceIdentifier(@onNull String name, @Nullable String defType, @Nullable String defPackage)797 @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType, 798 @Nullable String defPackage) { 799 synchronized (this) { 800 ensureValidLocked(); 801 // name is checked in JNI. 802 return nativeGetResourceIdentifier(mObject, name, defType, defPackage); 803 } 804 } 805 806 /** 807 * To get the parent theme resource id according to the parameter theme resource id. 808 * @param resId theme resource id. 809 * @return the parent theme resource id. 810 * @hide 811 */ 812 @StyleRes getParentThemeIdentifier(@tyleRes int resId)813 int getParentThemeIdentifier(@StyleRes int resId) { 814 synchronized (this) { 815 ensureValidLocked(); 816 // name is checked in JNI. 817 return nativeGetParentThemeIdentifier(mObject, resId); 818 } 819 } 820 821 /** 822 * Enable resource resolution logging to track the steps taken to resolve the last resource 823 * entry retrieved. Stores the configuration and package names for each step. 824 * 825 * Default disabled. 826 * 827 * @param enabled Boolean indicating whether to enable or disable logging. 828 * 829 * @hide 830 */ 831 @TestApi setResourceResolutionLoggingEnabled(boolean enabled)832 public void setResourceResolutionLoggingEnabled(boolean enabled) { 833 synchronized (this) { 834 ensureValidLocked(); 835 nativeSetResourceResolutionLoggingEnabled(mObject, enabled); 836 } 837 } 838 839 /** 840 * Retrieve the last resource resolution path logged. 841 * 842 * @return Formatted string containing last resource ID/name and steps taken to resolve final 843 * entry, including configuration and package names. 844 * 845 * @hide 846 */ 847 @TestApi getLastResourceResolution()848 public @Nullable String getLastResourceResolution() { 849 synchronized (this) { 850 ensureValidLocked(); 851 return nativeGetLastResourceResolution(mObject); 852 } 853 } 854 855 /** 856 * Returns whether the {@code resources.arsc} of any loaded apk assets is allocated in RAM 857 * (not mmapped). 858 * 859 * @hide 860 */ containsAllocatedTable()861 public boolean containsAllocatedTable() { 862 synchronized (this) { 863 ensureValidLocked(); 864 return nativeContainsAllocatedTable(mObject); 865 } 866 } 867 868 @Nullable getPooledStringForCookie(int cookie, int id)869 CharSequence getPooledStringForCookie(int cookie, int id) { 870 // Cookies map to ApkAssets starting at 1. 871 return getApkAssets()[cookie - 1].getStringFromPool(id); 872 } 873 874 /** 875 * Open an asset using ACCESS_STREAMING mode. This provides access to 876 * files that have been bundled with an application as assets -- that is, 877 * files placed in to the "assets" directory. 878 * 879 * @param fileName The name of the asset to open. This name can be hierarchical. 880 * 881 * @see #open(String, int) 882 * @see #list 883 */ open(@onNull String fileName)884 public @NonNull InputStream open(@NonNull String fileName) throws IOException { 885 return open(fileName, ACCESS_STREAMING); 886 } 887 888 /** 889 * Open an asset using an explicit access mode, returning an InputStream to 890 * read its contents. This provides access to files that have been bundled 891 * with an application as assets -- that is, files placed in to the 892 * "assets" directory. 893 * 894 * @param fileName The name of the asset to open. This name can be hierarchical. 895 * @param accessMode Desired access mode for retrieving the data. 896 * 897 * @see #ACCESS_UNKNOWN 898 * @see #ACCESS_STREAMING 899 * @see #ACCESS_RANDOM 900 * @see #ACCESS_BUFFER 901 * @see #open(String) 902 * @see #list 903 */ open(@onNull String fileName, int accessMode)904 public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException { 905 Objects.requireNonNull(fileName, "fileName"); 906 synchronized (this) { 907 ensureOpenLocked(); 908 final long asset = nativeOpenAsset(mObject, fileName, accessMode); 909 if (asset == 0) { 910 throw new FileNotFoundException("Asset file: " + fileName); 911 } 912 final AssetInputStream assetInputStream = new AssetInputStream(asset); 913 incRefsLocked(assetInputStream.hashCode()); 914 return assetInputStream; 915 } 916 } 917 918 /** 919 * Open an uncompressed asset by mmapping it and returning an {@link AssetFileDescriptor}. 920 * This provides access to files that have been bundled with an application as assets -- that 921 * is, files placed in to the "assets" directory. 922 * 923 * The asset must be uncompressed, or an exception will be thrown. 924 * 925 * @param fileName The name of the asset to open. This name can be hierarchical. 926 * @return An open AssetFileDescriptor. 927 */ openFd(@onNull String fileName)928 public @NonNull AssetFileDescriptor openFd(@NonNull String fileName) throws IOException { 929 Objects.requireNonNull(fileName, "fileName"); 930 synchronized (this) { 931 ensureOpenLocked(); 932 final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets); 933 if (pfd == null) { 934 throw new FileNotFoundException("Asset file: " + fileName); 935 } 936 return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); 937 } 938 } 939 940 /** 941 * Return a String array of all the assets at the given path. 942 * 943 * @param path A relative path within the assets, i.e., "docs/home.html". 944 * 945 * @return String[] Array of strings, one for each asset. These file 946 * names are relative to 'path'. You can open the file by 947 * concatenating 'path' and a name in the returned string (via 948 * File) and passing that to open(). 949 * 950 * @see #open 951 */ list(@onNull String path)952 public @Nullable String[] list(@NonNull String path) throws IOException { 953 Objects.requireNonNull(path, "path"); 954 synchronized (this) { 955 ensureValidLocked(); 956 return nativeList(mObject, path); 957 } 958 } 959 960 /** 961 * Open a non-asset file as an asset using ACCESS_STREAMING mode. This 962 * provides direct access to all of the files included in an application 963 * package (not only its assets). Applications should not normally use 964 * this. 965 * 966 * @param fileName Name of the asset to retrieve. 967 * 968 * @see #open(String) 969 * @hide 970 */ 971 @UnsupportedAppUsage openNonAsset(@onNull String fileName)972 public @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException { 973 return openNonAsset(0, fileName, ACCESS_STREAMING); 974 } 975 976 /** 977 * Open a non-asset file as an asset using a specific access mode. This 978 * provides direct access to all of the files included in an application 979 * package (not only its assets). Applications should not normally use 980 * this. 981 * 982 * @param fileName Name of the asset to retrieve. 983 * @param accessMode Desired access mode for retrieving the data. 984 * 985 * @see #ACCESS_UNKNOWN 986 * @see #ACCESS_STREAMING 987 * @see #ACCESS_RANDOM 988 * @see #ACCESS_BUFFER 989 * @see #open(String, int) 990 * @hide 991 */ 992 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) openNonAsset(@onNull String fileName, int accessMode)993 public @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode) 994 throws IOException { 995 return openNonAsset(0, fileName, accessMode); 996 } 997 998 /** 999 * Open a non-asset in a specified package. Not for use by applications. 1000 * 1001 * @param cookie Identifier of the package to be opened. 1002 * @param fileName Name of the asset to retrieve. 1003 * @hide 1004 */ 1005 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) openNonAsset(int cookie, @NonNull String fileName)1006 public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName) 1007 throws IOException { 1008 return openNonAsset(cookie, fileName, ACCESS_STREAMING); 1009 } 1010 1011 /** 1012 * Open a non-asset in a specified package. Not for use by applications. 1013 * 1014 * @param cookie Identifier of the package to be opened. 1015 * @param fileName Name of the asset to retrieve. 1016 * @param accessMode Desired access mode for retrieving the data. 1017 * @hide 1018 */ 1019 @UnsupportedAppUsage openNonAsset(int cookie, @NonNull String fileName, int accessMode)1020 public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode) 1021 throws IOException { 1022 Objects.requireNonNull(fileName, "fileName"); 1023 synchronized (this) { 1024 ensureOpenLocked(); 1025 final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode); 1026 if (asset == 0) { 1027 throw new FileNotFoundException("Asset absolute file: " + fileName); 1028 } 1029 final AssetInputStream assetInputStream = new AssetInputStream(asset); 1030 incRefsLocked(assetInputStream.hashCode()); 1031 return assetInputStream; 1032 } 1033 } 1034 1035 /** 1036 * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}. 1037 * This provides direct access to all of the files included in an application 1038 * package (not only its assets). Applications should not normally use this. 1039 * 1040 * The asset must not be compressed, or an exception will be thrown. 1041 * 1042 * @param fileName Name of the asset to retrieve. 1043 */ openNonAssetFd(@onNull String fileName)1044 public @NonNull AssetFileDescriptor openNonAssetFd(@NonNull String fileName) 1045 throws IOException { 1046 return openNonAssetFd(0, fileName); 1047 } 1048 1049 /** 1050 * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}. 1051 * This provides direct access to all of the files included in an application 1052 * package (not only its assets). Applications should not normally use this. 1053 * 1054 * The asset must not be compressed, or an exception will be thrown. 1055 * 1056 * @param cookie Identifier of the package to be opened. 1057 * @param fileName Name of the asset to retrieve. 1058 */ openNonAssetFd(int cookie, @NonNull String fileName)1059 public @NonNull AssetFileDescriptor openNonAssetFd(int cookie, @NonNull String fileName) 1060 throws IOException { 1061 Objects.requireNonNull(fileName, "fileName"); 1062 synchronized (this) { 1063 ensureOpenLocked(); 1064 final ParcelFileDescriptor pfd = 1065 nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets); 1066 if (pfd == null) { 1067 throw new FileNotFoundException("Asset absolute file: " + fileName); 1068 } 1069 return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); 1070 } 1071 } 1072 1073 /** 1074 * Retrieve a parser for a compiled XML file. 1075 * 1076 * @param fileName The name of the file to retrieve. 1077 */ openXmlResourceParser(@onNull String fileName)1078 public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName) 1079 throws IOException { 1080 return openXmlResourceParser(0, fileName); 1081 } 1082 1083 /** 1084 * Retrieve a parser for a compiled XML file. 1085 * 1086 * @param cookie Identifier of the package to be opened. 1087 * @param fileName The name of the file to retrieve. 1088 */ openXmlResourceParser(int cookie, @NonNull String fileName)1089 public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName) 1090 throws IOException { 1091 try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) { 1092 XmlResourceParser parser = block.newParser(); 1093 // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with 1094 // a valid native pointer, which makes newParser always return non-null. But let's 1095 // be careful. 1096 if (parser == null) { 1097 throw new AssertionError("block.newParser() returned a null parser"); 1098 } 1099 return parser; 1100 } 1101 } 1102 1103 /** 1104 * Retrieve a non-asset as a compiled XML file. Not for use by applications. 1105 * 1106 * @param fileName The name of the file to retrieve. 1107 * @hide 1108 */ openXmlBlockAsset(@onNull String fileName)1109 @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException { 1110 return openXmlBlockAsset(0, fileName); 1111 } 1112 1113 /** 1114 * Retrieve a non-asset as a compiled XML file. Not for use by 1115 * applications. 1116 * 1117 * @param cookie Identifier of the package to be opened. 1118 * @param fileName Name of the asset to retrieve. 1119 * @hide 1120 */ openXmlBlockAsset(int cookie, @NonNull String fileName)1121 @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException { 1122 Objects.requireNonNull(fileName, "fileName"); 1123 synchronized (this) { 1124 ensureOpenLocked(); 1125 1126 final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName); 1127 if (xmlBlock == 0) { 1128 throw new FileNotFoundException("Asset XML file: " + fileName); 1129 } 1130 final XmlBlock block = new XmlBlock(this, xmlBlock); 1131 incRefsLocked(block.hashCode()); 1132 return block; 1133 } 1134 } 1135 xmlBlockGone(int id)1136 void xmlBlockGone(int id) { 1137 synchronized (this) { 1138 decRefsLocked(id); 1139 } 1140 } 1141 1142 @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)1143 void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, 1144 @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress, 1145 long outIndicesAddress) { 1146 Objects.requireNonNull(inAttrs, "inAttrs"); 1147 synchronized (this) { 1148 // Need to synchronize on AssetManager because we will be accessing 1149 // the native implementation of AssetManager. 1150 ensureValidLocked(); 1151 nativeApplyStyle(mObject, themePtr, defStyleAttr, defStyleRes, 1152 parser != null ? parser.mParseState : 0, inAttrs, outValuesAddress, 1153 outIndicesAddress); 1154 } 1155 } 1156 getAttributeResolutionStack(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @StyleRes int xmlStyle)1157 int[] getAttributeResolutionStack(long themePtr, @AttrRes int defStyleAttr, 1158 @StyleRes int defStyleRes, @StyleRes int xmlStyle) { 1159 synchronized (this) { 1160 ensureValidLocked(); 1161 return nativeAttributeResolutionStack( 1162 mObject, themePtr, xmlStyle, defStyleAttr, defStyleRes); 1163 } 1164 } 1165 1166 @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)1167 boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, 1168 @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues, 1169 @NonNull int[] outIndices) { 1170 Objects.requireNonNull(inAttrs, "inAttrs"); 1171 Objects.requireNonNull(outValues, "outValues"); 1172 Objects.requireNonNull(outIndices, "outIndices"); 1173 synchronized (this) { 1174 // Need to synchronize on AssetManager because we will be accessing 1175 // the native implementation of AssetManager. 1176 ensureValidLocked(); 1177 return nativeResolveAttrs(mObject, 1178 themePtr, defStyleAttr, defStyleRes, inValues, inAttrs, outValues, outIndices); 1179 } 1180 } 1181 1182 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) retrieveAttributes(@onNull XmlBlock.Parser parser, @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices)1183 boolean retrieveAttributes(@NonNull XmlBlock.Parser parser, @NonNull int[] inAttrs, 1184 @NonNull int[] outValues, @NonNull int[] outIndices) { 1185 Objects.requireNonNull(parser, "parser"); 1186 Objects.requireNonNull(inAttrs, "inAttrs"); 1187 Objects.requireNonNull(outValues, "outValues"); 1188 Objects.requireNonNull(outIndices, "outIndices"); 1189 synchronized (this) { 1190 // Need to synchronize on AssetManager because we will be accessing 1191 // the native implementation of AssetManager. 1192 ensureValidLocked(); 1193 return nativeRetrieveAttributes( 1194 mObject, parser.mParseState, inAttrs, outValues, outIndices); 1195 } 1196 } 1197 1198 @UnsupportedAppUsage createTheme()1199 long createTheme() { 1200 synchronized (this) { 1201 ensureValidLocked(); 1202 long themePtr = nativeThemeCreate(mObject); 1203 incRefsLocked(themePtr); 1204 return themePtr; 1205 } 1206 } 1207 releaseTheme(long themePtr)1208 void releaseTheme(long themePtr) { 1209 synchronized (this) { 1210 decRefsLocked(themePtr); 1211 } 1212 } 1213 getThemeFreeFunction()1214 static long getThemeFreeFunction() { 1215 return nativeGetThemeFreeFunction(); 1216 } 1217 applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force)1218 void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) { 1219 synchronized (this) { 1220 // Need to synchronize on AssetManager because we will be accessing 1221 // the native implementation of AssetManager. 1222 ensureValidLocked(); 1223 nativeThemeApplyStyle(mObject, themePtr, resId, force); 1224 } 1225 } 1226 rebaseTheme(long themePtr, @NonNull AssetManager newAssetManager, @StyleRes int[] styleIds, @StyleRes boolean[] force, int count)1227 AssetManager rebaseTheme(long themePtr, @NonNull AssetManager newAssetManager, 1228 @StyleRes int[] styleIds, @StyleRes boolean[] force, int count) { 1229 // Exchange ownership of the theme with the new asset manager. 1230 if (this != newAssetManager) { 1231 synchronized (this) { 1232 ensureValidLocked(); 1233 decRefsLocked(themePtr); 1234 } 1235 synchronized (newAssetManager) { 1236 newAssetManager.ensureValidLocked(); 1237 newAssetManager.incRefsLocked(themePtr); 1238 } 1239 } 1240 1241 try { 1242 synchronized (newAssetManager) { 1243 newAssetManager.ensureValidLocked(); 1244 nativeThemeRebase(newAssetManager.mObject, themePtr, styleIds, force, count); 1245 } 1246 } finally { 1247 Reference.reachabilityFence(newAssetManager); 1248 } 1249 return newAssetManager; 1250 } 1251 1252 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr)1253 void setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr) { 1254 synchronized (this) { 1255 ensureValidLocked(); 1256 synchronized (srcAssetManager) { 1257 srcAssetManager.ensureValidLocked(); 1258 nativeThemeCopy(mObject, dstThemePtr, srcAssetManager.mObject, srcThemePtr); 1259 } 1260 } 1261 } 1262 1263 @Override finalize()1264 protected void finalize() throws Throwable { 1265 if (DEBUG_REFS && mNumRefs != 0) { 1266 Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs); 1267 if (mRefStacks != null) { 1268 for (RuntimeException e : mRefStacks.values()) { 1269 Log.w(TAG, "Reference from here", e); 1270 } 1271 } 1272 } 1273 1274 synchronized (this) { 1275 if (mObject != 0) { 1276 nativeDestroy(mObject); 1277 mObject = 0; 1278 } 1279 } 1280 } 1281 1282 /* No Locking is needed for AssetInputStream because an AssetInputStream is not-thread 1283 safe and it does not rely on AssetManager once it has been created. It completely owns the 1284 underlying Asset. */ 1285 public final class AssetInputStream extends InputStream { 1286 private long mAssetNativePtr; 1287 private long mLength; 1288 private long mMarkPos; 1289 1290 /** 1291 * @hide 1292 */ 1293 @UnsupportedAppUsage getAssetInt()1294 public final int getAssetInt() { 1295 throw new UnsupportedOperationException(); 1296 } 1297 1298 /** 1299 * @hide 1300 */ 1301 @UnsupportedAppUsage getNativeAsset()1302 public final long getNativeAsset() { 1303 return mAssetNativePtr; 1304 } 1305 AssetInputStream(long assetNativePtr)1306 private AssetInputStream(long assetNativePtr) { 1307 mAssetNativePtr = assetNativePtr; 1308 mLength = nativeAssetGetLength(assetNativePtr); 1309 } 1310 1311 @Override read()1312 public final int read() throws IOException { 1313 ensureOpen(); 1314 return nativeAssetReadChar(mAssetNativePtr); 1315 } 1316 1317 @Override read(@onNull byte[] b)1318 public final int read(@NonNull byte[] b) throws IOException { 1319 ensureOpen(); 1320 Objects.requireNonNull(b, "b"); 1321 return nativeAssetRead(mAssetNativePtr, b, 0, b.length); 1322 } 1323 1324 @Override read(@onNull byte[] b, int off, int len)1325 public final int read(@NonNull byte[] b, int off, int len) throws IOException { 1326 ensureOpen(); 1327 Objects.requireNonNull(b, "b"); 1328 return nativeAssetRead(mAssetNativePtr, b, off, len); 1329 } 1330 1331 @Override skip(long n)1332 public final long skip(long n) throws IOException { 1333 ensureOpen(); 1334 long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); 1335 if ((pos + n) > mLength) { 1336 n = mLength - pos; 1337 } 1338 if (n > 0) { 1339 nativeAssetSeek(mAssetNativePtr, n, 0); 1340 } 1341 return n; 1342 } 1343 1344 @Override available()1345 public final int available() throws IOException { 1346 ensureOpen(); 1347 final long len = nativeAssetGetRemainingLength(mAssetNativePtr); 1348 return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; 1349 } 1350 1351 @Override markSupported()1352 public final boolean markSupported() { 1353 return true; 1354 } 1355 1356 @Override mark(int readlimit)1357 public final void mark(int readlimit) { 1358 ensureOpen(); 1359 mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); 1360 } 1361 1362 @Override reset()1363 public final void reset() throws IOException { 1364 ensureOpen(); 1365 nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); 1366 } 1367 1368 @Override close()1369 public final void close() throws IOException { 1370 if (mAssetNativePtr != 0) { 1371 nativeAssetDestroy(mAssetNativePtr); 1372 mAssetNativePtr = 0; 1373 1374 synchronized (AssetManager.this) { 1375 decRefsLocked(hashCode()); 1376 } 1377 } 1378 } 1379 1380 @Override finalize()1381 protected void finalize() throws Throwable { 1382 close(); 1383 } 1384 ensureOpen()1385 private void ensureOpen() { 1386 if (mAssetNativePtr == 0) { 1387 throw new IllegalStateException("AssetInputStream is closed"); 1388 } 1389 } 1390 } 1391 1392 /** 1393 * Determine whether the state in this asset manager is up-to-date with 1394 * the files on the filesystem. If false is returned, you need to 1395 * instantiate a new AssetManager class to see the new data. 1396 * @hide 1397 */ 1398 @UnsupportedAppUsage isUpToDate()1399 public boolean isUpToDate() { 1400 synchronized (this) { 1401 if (!mOpen) { 1402 return false; 1403 } 1404 1405 for (ApkAssets apkAssets : mApkAssets) { 1406 if (!apkAssets.isUpToDate()) { 1407 return false; 1408 } 1409 } 1410 1411 return true; 1412 } 1413 } 1414 1415 /** 1416 * Get the locales that this asset manager contains data for. 1417 * 1418 * <p>On SDK 21 (Android 5.0: Lollipop) and above, Locale strings are valid 1419 * <a href="https://tools.ietf.org/html/bcp47">BCP-47</a> language tags and can be 1420 * parsed using {@link Locale#forLanguageTag(String)}. 1421 * 1422 * <p>On SDK 20 (Android 4.4W: Kitkat for watches) and below, locale strings 1423 * are of the form {@code ll_CC} where {@code ll} is a two letter language code, 1424 * and {@code CC} is a two letter country code. 1425 */ getLocales()1426 public String[] getLocales() { 1427 synchronized (this) { 1428 ensureValidLocked(); 1429 return nativeGetLocales(mObject, false /*excludeSystem*/); 1430 } 1431 } 1432 1433 /** 1434 * Same as getLocales(), except that locales that are only provided by the system (i.e. those 1435 * present in framework-res.apk or its overlays) will not be listed. 1436 * 1437 * For example, if the "system" assets support English, French, and German, and the additional 1438 * assets support Cherokee and French, getLocales() would return 1439 * [Cherokee, English, French, German], while getNonSystemLocales() would return 1440 * [Cherokee, French]. 1441 * @hide 1442 */ getNonSystemLocales()1443 public String[] getNonSystemLocales() { 1444 synchronized (this) { 1445 ensureValidLocked(); 1446 return nativeGetLocales(mObject, true /*excludeSystem*/); 1447 } 1448 } 1449 1450 /** 1451 * @hide 1452 */ getSizeConfigurations()1453 Configuration[] getSizeConfigurations() { 1454 synchronized (this) { 1455 ensureValidLocked(); 1456 return nativeGetSizeConfigurations(mObject); 1457 } 1458 } 1459 1460 /** 1461 * @hide 1462 */ getSizeAndUiModeConfigurations()1463 Configuration[] getSizeAndUiModeConfigurations() { 1464 synchronized (this) { 1465 ensureValidLocked(); 1466 return nativeGetSizeAndUiModeConfigurations(mObject); 1467 } 1468 } 1469 1470 /** 1471 * Change the configuration used when retrieving resources. Not for use by 1472 * applications. 1473 * @hide 1474 */ 1475 @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 grammaticalGender, int majorVersion)1476 public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, 1477 int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, 1478 int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, 1479 int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender, 1480 int majorVersion) { 1481 synchronized (this) { 1482 ensureValidLocked(); 1483 nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, 1484 keyboard, keyboardHidden, navigation, screenWidth, screenHeight, 1485 smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, 1486 colorMode, grammaticalGender, majorVersion); 1487 } 1488 } 1489 1490 /** 1491 * @hide 1492 */ 1493 @UnsupportedAppUsage getAssignedPackageIdentifiers()1494 public SparseArray<String> getAssignedPackageIdentifiers() { 1495 return getAssignedPackageIdentifiers(true, true); 1496 } 1497 1498 /** 1499 * @hide 1500 */ getAssignedPackageIdentifiers(boolean includeOverlays, boolean includeLoaders)1501 public SparseArray<String> getAssignedPackageIdentifiers(boolean includeOverlays, 1502 boolean includeLoaders) { 1503 synchronized (this) { 1504 ensureValidLocked(); 1505 return nativeGetAssignedPackageIdentifiers(mObject, includeOverlays, includeLoaders); 1506 } 1507 } 1508 1509 /** 1510 * @hide 1511 */ 1512 @GuardedBy("this") getOverlayableMap(String packageName)1513 public @Nullable Map<String, String> getOverlayableMap(String packageName) { 1514 synchronized (this) { 1515 ensureValidLocked(); 1516 return nativeGetOverlayableMap(mObject, packageName); 1517 } 1518 } 1519 1520 /** 1521 * @hide 1522 */ 1523 @TestApi 1524 @GuardedBy("this") getOverlayablesToString(String packageName)1525 public @Nullable String getOverlayablesToString(String packageName) { 1526 synchronized (this) { 1527 ensureValidLocked(); 1528 return nativeGetOverlayablesToString(mObject, packageName); 1529 } 1530 } 1531 1532 @GuardedBy("this") incRefsLocked(long id)1533 private void incRefsLocked(long id) { 1534 if (DEBUG_REFS) { 1535 if (mRefStacks == null) { 1536 mRefStacks = new HashMap<>(); 1537 } 1538 RuntimeException ex = new RuntimeException(); 1539 ex.fillInStackTrace(); 1540 mRefStacks.put(id, ex); 1541 } 1542 mNumRefs++; 1543 } 1544 1545 @GuardedBy("this") decRefsLocked(long id)1546 private void decRefsLocked(long id) { 1547 if (DEBUG_REFS && mRefStacks != null) { 1548 mRefStacks.remove(id); 1549 } 1550 mNumRefs--; 1551 if (mNumRefs == 0 && mObject != 0) { 1552 nativeDestroy(mObject); 1553 mObject = 0; 1554 mApkAssets = sEmptyApkAssets; 1555 } 1556 } 1557 dump(PrintWriter pw, String prefix)1558 synchronized void dump(PrintWriter pw, String prefix) { 1559 pw.println(prefix + "class=" + getClass()); 1560 pw.println(prefix + "apkAssets="); 1561 for (int i = 0; i < mApkAssets.length; i++) { 1562 pw.println(prefix + i); 1563 mApkAssets[i].dump(pw, prefix + " "); 1564 } 1565 } 1566 1567 // AssetManager setup native methods. nativeCreate()1568 private static native long nativeCreate(); nativeDestroy(long ptr)1569 private static native void nativeDestroy(long ptr); nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, boolean invalidateCaches)1570 private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, 1571 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 grammaticalGender, int majorVersion)1572 private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, 1573 @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, 1574 int keyboardHidden, int navigation, int screenWidth, int screenHeight, 1575 int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, 1576 int uiMode, int colorMode, int grammaticalGender, int majorVersion); nativeGetAssignedPackageIdentifiers( long ptr, boolean includeOverlays, boolean includeLoaders)1577 private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers( 1578 long ptr, boolean includeOverlays, boolean includeLoaders); 1579 1580 // File native methods. nativeContainsAllocatedTable(long ptr)1581 private static native boolean nativeContainsAllocatedTable(long ptr); nativeList(long ptr, @NonNull String path)1582 private static native @Nullable String[] nativeList(long ptr, @NonNull String path) 1583 throws IOException; nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode)1584 private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); nativeOpenAssetFd(long ptr, @NonNull String fileName, long[] outOffsets)1585 private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, 1586 @NonNull String fileName, long[] outOffsets) throws IOException; nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, int accessMode)1587 private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, 1588 int accessMode); nativeOpenNonAssetFd(long ptr, int cookie, @NonNull String fileName, @NonNull long[] outOffsets)1589 private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, 1590 @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName)1591 private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); nativeOpenXmlAssetFd(long ptr, int cookie, @NonNull FileDescriptor fileDescriptor)1592 private static native long nativeOpenXmlAssetFd(long ptr, int cookie, 1593 @NonNull FileDescriptor fileDescriptor); 1594 1595 // Primitive resource native methods. nativeGetResourceValue(long ptr, @AnyRes int resId, short density, @NonNull TypedValue outValue, boolean resolveReferences)1596 private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, 1597 @NonNull TypedValue outValue, boolean resolveReferences); nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, @NonNull TypedValue outValue)1598 private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, 1599 @NonNull TypedValue outValue); 1600 nativeGetStyleAttributes(long ptr, @StyleRes int resId)1601 private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, 1602 @StyleRes int resId); nativeGetResourceStringArray(long ptr, @ArrayRes int resId)1603 private static native @Nullable String[] nativeGetResourceStringArray(long ptr, 1604 @ArrayRes int resId); nativeGetResourceStringArrayInfo(long ptr, @ArrayRes int resId)1605 private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, 1606 @ArrayRes int resId); nativeGetResourceIntArray(long ptr, @ArrayRes int resId)1607 private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); nativeGetResourceArraySize(long ptr, @ArrayRes int resId)1608 private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); nativeGetResourceArray(long ptr, @ArrayRes int resId, @NonNull int[] outValues)1609 private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, 1610 @NonNull int[] outValues); 1611 1612 // Resource name/ID native methods. nativeGetResourceIdentifier(long ptr, @NonNull String name, @Nullable String defType, @Nullable String defPackage)1613 private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, 1614 @Nullable String defType, @Nullable String defPackage); nativeGetResourceName(long ptr, @AnyRes int resid)1615 private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); nativeGetResourcePackageName(long ptr, @AnyRes int resid)1616 private static native @Nullable String nativeGetResourcePackageName(long ptr, 1617 @AnyRes int resid); nativeGetResourceTypeName(long ptr, @AnyRes int resid)1618 private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); nativeGetResourceEntryName(long ptr, @AnyRes int resid)1619 private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); nativeGetLocales(long ptr, boolean excludeSystem)1620 private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); nativeGetSizeConfigurations(long ptr)1621 private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); nativeGetSizeAndUiModeConfigurations(long ptr)1622 private static native @Nullable Configuration[] nativeGetSizeAndUiModeConfigurations(long ptr); nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled)1623 private static native void nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled); nativeGetLastResourceResolution(long ptr)1624 private static native @Nullable String nativeGetLastResourceResolution(long ptr); 1625 1626 // Style attribute retrieval native methods. nativeAttributeResolutionStack(long ptr, long themePtr, @StyleRes int xmlStyleRes, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)1627 private static native int[] nativeAttributeResolutionStack(long ptr, long themePtr, 1628 @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)1629 private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, 1630 @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, 1631 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)1632 private static native boolean nativeResolveAttrs(long ptr, long themePtr, 1633 @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, 1634 @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); nativeRetrieveAttributes(long ptr, long xmlParserPtr, @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices)1635 private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, 1636 @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); 1637 1638 // Theme related native methods nativeThemeCreate(long ptr)1639 private static native long nativeThemeCreate(long ptr); nativeGetThemeFreeFunction()1640 private static native long nativeGetThemeFreeFunction(); nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, boolean force)1641 private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, 1642 boolean force); nativeThemeRebase(long ptr, long themePtr, @NonNull int[] styleIds, @NonNull boolean[] force, int styleSize)1643 private static native void nativeThemeRebase(long ptr, long themePtr, @NonNull int[] styleIds, 1644 @NonNull boolean[] force, int styleSize); nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr, long srcAssetManagerPtr, long srcThemePtr)1645 private static native void nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr, 1646 long srcAssetManagerPtr, long srcThemePtr); nativeThemeGetAttributeValue(long ptr, long themePtr, @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve)1647 private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, 1648 @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); nativeThemeDump(long ptr, long themePtr, int priority, String tag, String prefix)1649 private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, 1650 String prefix); nativeThemeGetChangingConfigurations(long themePtr)1651 static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); 1652 @StyleRes nativeGetParentThemeIdentifier(long ptr, @StyleRes int styleId)1653 private static native int nativeGetParentThemeIdentifier(long ptr, @StyleRes int styleId); 1654 1655 // AssetInputStream related native methods. nativeAssetDestroy(long assetPtr)1656 private static native void nativeAssetDestroy(long assetPtr); nativeAssetReadChar(long assetPtr)1657 private static native int nativeAssetReadChar(long assetPtr); nativeAssetRead(long assetPtr, byte[] b, int off, int len)1658 private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); nativeAssetSeek(long assetPtr, long offset, int whence)1659 private static native long nativeAssetSeek(long assetPtr, long offset, int whence); nativeAssetGetLength(long assetPtr)1660 private static native long nativeAssetGetLength(long assetPtr); nativeAssetGetRemainingLength(long assetPtr)1661 private static native long nativeAssetGetRemainingLength(long assetPtr); 1662 nativeGetOverlayableMap(long ptr, @NonNull String packageName)1663 private static native @Nullable Map nativeGetOverlayableMap(long ptr, 1664 @NonNull String packageName); nativeGetOverlayablesToString(long ptr, @NonNull String packageName)1665 private static native @Nullable String nativeGetOverlayablesToString(long ptr, 1666 @NonNull String packageName); 1667 1668 // Global debug native methods. 1669 /** 1670 * @hide 1671 */ 1672 @UnsupportedAppUsage getGlobalAssetCount()1673 public static native int getGlobalAssetCount(); 1674 1675 /** 1676 * @hide 1677 */ getAssetAllocations()1678 public static native String getAssetAllocations(); 1679 1680 /** 1681 * @hide 1682 */ 1683 @UnsupportedAppUsage getGlobalAssetManagerCount()1684 public static native int getGlobalAssetManagerCount(); 1685 } 1686