AssetManager.java revision 8cae124af2142687a6833dbaab8a43df6dd67b43
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 17package android.content.res; 18 19import android.os.ParcelFileDescriptor; 20import android.util.Config; 21import android.util.Log; 22import android.util.TypedValue; 23 24import java.io.FileNotFoundException; 25import java.io.IOException; 26import java.io.InputStream; 27 28/** 29 * Provides access to an application's raw asset files; see {@link Resources} 30 * for the way most applications will want to retrieve their resource data. 31 * This class presents a lower-level API that allows you to open and read raw 32 * files that have been bundled with the application as a simple stream of 33 * bytes. 34 */ 35public final class AssetManager { 36 /* modes used when opening an asset */ 37 38 /** 39 * Mode for {@link #open(String, int)}: no specific information about how 40 * data will be accessed. 41 */ 42 public static final int ACCESS_UNKNOWN = 0; 43 /** 44 * Mode for {@link #open(String, int)}: Read chunks, and seek forward and 45 * backward. 46 */ 47 public static final int ACCESS_RANDOM = 1; 48 /** 49 * Mode for {@link #open(String, int)}: Read sequentially, with an 50 * occasional forward seek. 51 */ 52 public static final int ACCESS_STREAMING = 2; 53 /** 54 * Mode for {@link #open(String, int)}: Attempt to load contents into 55 * memory, for fast small reads. 56 */ 57 public static final int ACCESS_BUFFER = 3; 58 59 private static final String TAG = "AssetManager"; 60 private static final boolean localLOGV = Config.LOGV || false; 61 62 private static final Object mSync = new Object(); 63 private static final TypedValue mValue = new TypedValue(); 64 private static final long[] mOffsets = new long[2]; 65 private static AssetManager mSystem = null; 66 67 // For communication with native code. 68 private int mObject; 69 70 private StringBlock mStringBlocks[] = null; 71 72 private int mNumRefs = 1; 73 private boolean mOpen = true; 74 private String mAssetDir; 75 private String mAppName; 76 77 /** 78 * Create a new AssetManager containing only the basic system assets. 79 * Applications will not generally use this method, instead retrieving the 80 * appropriate asset manager with {@link Resources#getAssets}. Not for 81 * use by applications. 82 * {@hide} 83 */ 84 public AssetManager() { 85 synchronized (mSync) { 86 init(); 87 if (localLOGV) Log.v(TAG, "New asset manager: " + this); 88 ensureSystemAssets(); 89 } 90 } 91 92 private static void ensureSystemAssets() { 93 synchronized (mSync) { 94 if (mSystem == null) { 95 AssetManager system = new AssetManager(true); 96 system.makeStringBlocks(false); 97 mSystem = system; 98 } 99 } 100 } 101 102 private AssetManager(boolean isSystem) { 103 init(); 104 if (localLOGV) Log.v(TAG, "New asset manager: " + this); 105 } 106 107 /** 108 * Return a global shared asset manager that provides access to only 109 * system assets (no application assets). 110 * {@hide} 111 */ 112 public static AssetManager getSystem() { 113 ensureSystemAssets(); 114 return mSystem; 115 } 116 117 /** 118 * Close this asset manager. 119 */ 120 public void close() { 121 synchronized(mSync) { 122 //System.out.println("Release: num=" + mNumRefs 123 // + ", released=" + mReleased); 124 if (mOpen) { 125 mOpen = false; 126 decRefsLocked(); 127 } 128 } 129 } 130 131 /** 132 * Retrieve the string value associated with a particular resource 133 * identifier for the current configuration / skin. 134 */ 135 /*package*/ final CharSequence getResourceText(int ident) { 136 synchronized (mSync) { 137 TypedValue tmpValue = mValue; 138 int block = loadResourceValue(ident, tmpValue, true); 139 if (block >= 0) { 140 if (tmpValue.type == TypedValue.TYPE_STRING) { 141 return mStringBlocks[block].get(tmpValue.data); 142 } 143 return tmpValue.coerceToString(); 144 } 145 } 146 return null; 147 } 148 149 /** 150 * Retrieve the string value associated with a particular resource 151 * identifier for the current configuration / skin. 152 */ 153 /*package*/ final CharSequence getResourceBagText(int ident, int bagEntryId) { 154 synchronized (mSync) { 155 TypedValue tmpValue = mValue; 156 int block = loadResourceBagValue(ident, bagEntryId, tmpValue, true); 157 if (block >= 0) { 158 if (tmpValue.type == TypedValue.TYPE_STRING) { 159 return mStringBlocks[block].get(tmpValue.data); 160 } 161 return tmpValue.coerceToString(); 162 } 163 } 164 return null; 165 } 166 167 /** 168 * Retrieve the string array associated with a particular resource 169 * identifier. 170 * @param id Resource id of the string array 171 */ 172 /*package*/ final String[] getResourceStringArray(final int id) { 173 String[] retArray = getArrayStringResource(id); 174 return retArray; 175 } 176 177 178 /*package*/ final boolean getResourceValue(int ident, 179 TypedValue outValue, 180 boolean resolveRefs) 181 { 182 int block = loadResourceValue(ident, outValue, resolveRefs); 183 if (block >= 0) { 184 if (outValue.type != TypedValue.TYPE_STRING) { 185 return true; 186 } 187 outValue.string = mStringBlocks[block].get(outValue.data); 188 return true; 189 } 190 return false; 191 } 192 193 /** 194 * Retrieve the text array associated with a particular resource 195 * identifier. 196 * @param id Resource id of the string array 197 */ 198 /*package*/ final CharSequence[] getResourceTextArray(final int id) { 199 int[] rawInfoArray = getArrayStringInfo(id); 200 int rawInfoArrayLen = rawInfoArray.length; 201 final int infoArrayLen = rawInfoArrayLen / 2; 202 int block; 203 int index; 204 CharSequence[] retArray = new CharSequence[infoArrayLen]; 205 for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { 206 block = rawInfoArray[i]; 207 index = rawInfoArray[i + 1]; 208 retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; 209 } 210 return retArray; 211 } 212 213 /*package*/ final boolean getThemeValue(int theme, int ident, 214 TypedValue outValue, boolean resolveRefs) { 215 int block = loadThemeAttributeValue(theme, ident, outValue, resolveRefs); 216 if (block >= 0) { 217 if (outValue.type != TypedValue.TYPE_STRING) { 218 return true; 219 } 220 StringBlock[] blocks = mStringBlocks; 221 if (blocks == null) { 222 ensureStringBlocks(); 223 } 224 outValue.string = blocks[block].get(outValue.data); 225 return true; 226 } 227 return false; 228 } 229 230 /*package*/ final void ensureStringBlocks() { 231 if (mStringBlocks == null) { 232 synchronized (mSync) { 233 if (mStringBlocks == null) { 234 makeStringBlocks(true); 235 } 236 } 237 } 238 } 239 240 private final void makeStringBlocks(boolean copyFromSystem) { 241 final int sysNum = copyFromSystem ? mSystem.mStringBlocks.length : 0; 242 final int num = getStringBlockCount(); 243 mStringBlocks = new StringBlock[num]; 244 if (localLOGV) Log.v(TAG, "Making string blocks for " + this 245 + ": " + num); 246 for (int i=0; i<num; i++) { 247 if (i < sysNum) { 248 mStringBlocks[i] = mSystem.mStringBlocks[i]; 249 } else { 250 mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true); 251 } 252 } 253 } 254 255 /*package*/ final CharSequence getPooledString(int block, int id) { 256 //System.out.println("Get pooled: block=" + block 257 // + ", id=#" + Integer.toHexString(id) 258 // + ", blocks=" + mStringBlocks); 259 return mStringBlocks[block-1].get(id); 260 } 261 262 /** 263 * Open an asset using ACCESS_STREAMING mode. This provides access to 264 * files that have been bundled with an application as assets -- that is, 265 * files placed in to the "assets" directory. 266 * 267 * @param fileName The name of the asset to open. This name can be 268 * hierarchical. 269 * 270 * @see #open(String, int) 271 * @see #list 272 */ 273 public final InputStream open(String fileName) throws IOException { 274 return open(fileName, ACCESS_STREAMING); 275 } 276 277 /** 278 * Open an asset using an explicit access mode, returning an InputStream to 279 * read its contents. This provides access to files that have been bundled 280 * with an application as assets -- that is, files placed in to the 281 * "assets" directory. 282 * 283 * @param fileName The name of the asset to open. This name can be 284 * hierarchical. 285 * @param accessMode Desired access mode for retrieving the data. 286 * 287 * @see #ACCESS_UNKNOWN 288 * @see #ACCESS_STREAMING 289 * @see #ACCESS_RANDOM 290 * @see #ACCESS_BUFFER 291 * @see #open(String) 292 * @see #list 293 */ 294 public final InputStream open(String fileName, int accessMode) 295 throws IOException { 296 synchronized (mSync) { 297 if (!mOpen) { 298 throw new RuntimeException("Assetmanager has been closed"); 299 } 300 int asset = openAsset(fileName, accessMode); 301 if (asset != 0) { 302 mNumRefs++; 303 return new AssetInputStream(asset); 304 } 305 } 306 throw new FileNotFoundException("Asset file: " + fileName); 307 } 308 309 public final AssetFileDescriptor openFd(String fileName) 310 throws IOException { 311 synchronized (mSync) { 312 if (!mOpen) { 313 throw new RuntimeException("Assetmanager has been closed"); 314 } 315 ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets); 316 if (pfd != null) { 317 return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); 318 } 319 } 320 throw new FileNotFoundException("Asset file: " + fileName); 321 } 322 323 /** 324 * Return a String array of all the assets at the given path. 325 * 326 * @param path A relative path within the assets, i.e., "docs/home.html". 327 * 328 * @return String[] Array of strings, one for each asset. These file 329 * names are relative to 'path'. You can open the file by 330 * concatenating 'path' and a name in the returned string (via 331 * File) and passing that to open(). 332 * 333 * @see #open 334 */ 335 public native final String[] list(String path) 336 throws IOException; 337 338 /** 339 * {@hide} 340 * Open a non-asset file as an asset using ACCESS_STREAMING mode. This 341 * provides direct access to all of the files included in an application 342 * package (not only its assets). Applications should not normally use 343 * this. 344 * 345 * @see #open(String) 346 */ 347 public final InputStream openNonAsset(String fileName) throws IOException { 348 return openNonAsset(0, fileName, ACCESS_STREAMING); 349 } 350 351 /** 352 * {@hide} 353 * Open a non-asset file as an asset using a specific access mode. This 354 * provides direct access to all of the files included in an application 355 * package (not only its assets). Applications should not normally use 356 * this. 357 * 358 * @see #open(String, int) 359 */ 360 public final InputStream openNonAsset(String fileName, int accessMode) 361 throws IOException { 362 return openNonAsset(0, fileName, accessMode); 363 } 364 365 /** 366 * {@hide} 367 * Open a non-asset in a specified package. Not for use by applications. 368 * 369 * @param cookie Identifier of the package to be opened. 370 * @param fileName Name of the asset to retrieve. 371 */ 372 public final InputStream openNonAsset(int cookie, String fileName) 373 throws IOException { 374 return openNonAsset(cookie, fileName, ACCESS_STREAMING); 375 } 376 377 /** 378 * {@hide} 379 * Open a non-asset in a specified package. Not for use by applications. 380 * 381 * @param cookie Identifier of the package to be opened. 382 * @param fileName Name of the asset to retrieve. 383 * @param accessMode Desired access mode for retrieving the data. 384 */ 385 public final InputStream openNonAsset(int cookie, String fileName, int accessMode) 386 throws IOException { 387 synchronized (mSync) { 388 if (!mOpen) { 389 throw new RuntimeException("Assetmanager has been closed"); 390 } 391 int asset = openNonAssetNative(cookie, fileName, accessMode); 392 if (asset != 0) { 393 mNumRefs++; 394 return new AssetInputStream(asset); 395 } 396 } 397 throw new FileNotFoundException("Asset absolute file: " + fileName); 398 } 399 400 public final AssetFileDescriptor openNonAssetFd(String fileName) 401 throws IOException { 402 return openNonAssetFd(0, fileName); 403 } 404 405 public final AssetFileDescriptor openNonAssetFd(int cookie, 406 String fileName) throws IOException { 407 synchronized (mSync) { 408 if (!mOpen) { 409 throw new RuntimeException("Assetmanager has been closed"); 410 } 411 ParcelFileDescriptor pfd = openNonAssetFdNative(cookie, 412 fileName, mOffsets); 413 if (pfd != null) { 414 return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); 415 } 416 } 417 throw new FileNotFoundException("Asset absolute file: " + fileName); 418 } 419 420 /** 421 * Retrieve a parser for a compiled XML file. 422 * 423 * @param fileName The name of the file to retrieve. 424 */ 425 public final XmlResourceParser openXmlResourceParser(String fileName) 426 throws IOException { 427 return openXmlResourceParser(0, fileName); 428 } 429 430 /** 431 * Retrieve a parser for a compiled XML file. 432 * 433 * @param cookie Identifier of the package to be opened. 434 * @param fileName The name of the file to retrieve. 435 */ 436 public final XmlResourceParser openXmlResourceParser(int cookie, 437 String fileName) throws IOException { 438 XmlBlock block = openXmlBlockAsset(cookie, fileName); 439 XmlResourceParser rp = block.newParser(); 440 block.close(); 441 return rp; 442 } 443 444 /** 445 * {@hide} 446 * Retrieve a non-asset as a compiled XML file. Not for use by 447 * applications. 448 * 449 * @param fileName The name of the file to retrieve. 450 */ 451 /*package*/ final XmlBlock openXmlBlockAsset(String fileName) 452 throws IOException { 453 return openXmlBlockAsset(0, fileName); 454 } 455 456 /** 457 * {@hide} 458 * Retrieve a non-asset as a compiled XML file. Not for use by 459 * applications. 460 * 461 * @param cookie Identifier of the package to be opened. 462 * @param fileName Name of the asset to retrieve. 463 */ 464 /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName) 465 throws IOException { 466 synchronized (mSync) { 467 if (!mOpen) { 468 throw new RuntimeException("Assetmanager has been closed"); 469 } 470 int xmlBlock = openXmlAssetNative(cookie, fileName); 471 if (xmlBlock != 0) { 472 mNumRefs++; 473 return new XmlBlock(this, xmlBlock); 474 } 475 } 476 throw new FileNotFoundException("Asset XML file: " + fileName); 477 } 478 479 /*package*/ void xmlBlockGone() { 480 synchronized (mSync) { 481 decRefsLocked(); 482 } 483 } 484 485 /*package*/ final int createTheme() { 486 synchronized (mSync) { 487 if (!mOpen) { 488 throw new RuntimeException("Assetmanager has been closed"); 489 } 490 mNumRefs++; 491 return newTheme(); 492 } 493 } 494 495 /*package*/ final void releaseTheme(int theme) { 496 synchronized (mSync) { 497 deleteTheme(theme); 498 decRefsLocked(); 499 } 500 } 501 502 protected void finalize() throws Throwable { 503 destroy(); 504 } 505 506 public final class AssetInputStream extends InputStream { 507 public final int getAssetInt() { 508 return mAsset; 509 } 510 private AssetInputStream(int asset) 511 { 512 mAsset = asset; 513 mLength = getAssetLength(asset); 514 } 515 public final int read() throws IOException { 516 return readAssetChar(mAsset); 517 } 518 public final boolean markSupported() { 519 return true; 520 } 521 public final int available() throws IOException { 522 long len = getAssetRemainingLength(mAsset); 523 return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; 524 } 525 public final void close() throws IOException { 526 synchronized (AssetManager.mSync) { 527 if (mAsset != 0) { 528 destroyAsset(mAsset); 529 mAsset = 0; 530 decRefsLocked(); 531 } 532 } 533 } 534 public final void mark(int readlimit) { 535 mMarkPos = seekAsset(mAsset, 0, 0); 536 } 537 public final void reset() throws IOException { 538 seekAsset(mAsset, mMarkPos, -1); 539 } 540 public final int read(byte[] b) throws IOException { 541 return readAsset(mAsset, b, 0, b.length); 542 } 543 public final int read(byte[] b, int off, int len) throws IOException { 544 return readAsset(mAsset, b, off, len); 545 } 546 public final long skip(long n) throws IOException { 547 long pos = seekAsset(mAsset, 0, 0); 548 if ((pos+n) > mLength) { 549 n = mLength-pos; 550 } 551 if (n > 0) { 552 seekAsset(mAsset, n, 0); 553 } 554 return n; 555 } 556 557 protected void finalize() throws Throwable 558 { 559 close(); 560 } 561 562 private int mAsset; 563 private long mLength; 564 private long mMarkPos; 565 } 566 567 /** 568 * Add an additional set of assets to the asset manager. This can be 569 * either a directory or ZIP file. Not for use by applications. Returns 570 * the cookie of the added asset, or 0 on failure. 571 * {@hide} 572 */ 573 public native final int addAssetPath(String path); 574 575 /** 576 * Determine whether the state in this asset manager is up-to-date with 577 * the files on the filesystem. If false is returned, you need to 578 * instantiate a new AssetManager class to see the new data. 579 * {@hide} 580 */ 581 public native final boolean isUpToDate(); 582 583 /** 584 * Change the locale being used by this asset manager. Not for use by 585 * applications. 586 * {@hide} 587 */ 588 public native final void setLocale(String locale); 589 590 /** 591 * Get the locales that this asset manager contains data for. 592 */ 593 public native final String[] getLocales(); 594 595 /** 596 * Change the configuation used when retrieving resources. Not for use by 597 * applications. 598 * {@hide} 599 */ 600 public native final void setConfiguration(int mcc, int mnc, String locale, 601 int orientation, int touchscreen, int density, int keyboard, 602 int keyboardHidden, int navigation, int screenWidth, int screenHeight, 603 int screenLayout, int majorVersion); 604 605 /** 606 * Retrieve the resource identifier for the given resource name. 607 */ 608 /*package*/ native final int getResourceIdentifier(String type, 609 String name, 610 String defPackage); 611 612 /*package*/ native final String getResourceName(int resid); 613 /*package*/ native final String getResourcePackageName(int resid); 614 /*package*/ native final String getResourceTypeName(int resid); 615 /*package*/ native final String getResourceEntryName(int resid); 616 617 private native final int openAsset(String fileName, int accessMode); 618 private final native ParcelFileDescriptor openAssetFd(String fileName, 619 long[] outOffsets) throws IOException; 620 private native final int openNonAssetNative(int cookie, String fileName, 621 int accessMode); 622 private native ParcelFileDescriptor openNonAssetFdNative(int cookie, 623 String fileName, long[] outOffsets) throws IOException; 624 private native final void destroyAsset(int asset); 625 private native final int readAssetChar(int asset); 626 private native final int readAsset(int asset, byte[] b, int off, int len); 627 private native final long seekAsset(int asset, long offset, int whence); 628 private native final long getAssetLength(int asset); 629 private native final long getAssetRemainingLength(int asset); 630 631 /** Returns true if the resource was found, filling in mRetStringBlock and 632 * mRetData. */ 633 private native final int loadResourceValue(int ident, TypedValue outValue, 634 boolean resolve); 635 /** Returns true if the resource was found, filling in mRetStringBlock and 636 * mRetData. */ 637 private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, 638 boolean resolve); 639 /*package*/ static final int STYLE_NUM_ENTRIES = 6; 640 /*package*/ static final int STYLE_TYPE = 0; 641 /*package*/ static final int STYLE_DATA = 1; 642 /*package*/ static final int STYLE_ASSET_COOKIE = 2; 643 /*package*/ static final int STYLE_RESOURCE_ID = 3; 644 /*package*/ static final int STYLE_CHANGING_CONFIGURATIONS = 4; 645 /*package*/ static final int STYLE_DENSITY = 5; 646 /*package*/ native static final boolean applyStyle(int theme, 647 int defStyleAttr, int defStyleRes, int xmlParser, 648 int[] inAttrs, int[] outValues, int[] outIndices); 649 /*package*/ native final boolean retrieveAttributes( 650 int xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); 651 /*package*/ native final int getArraySize(int resource); 652 /*package*/ native final int retrieveArray(int resource, int[] outValues); 653 private native final int getStringBlockCount(); 654 private native final int getNativeStringBlock(int block); 655 656 /** 657 * {@hide} 658 */ 659 public native final String getCookieName(int cookie); 660 661 /** 662 * {@hide} 663 */ 664 public native static final int getGlobalAssetCount(); 665 666 /** 667 * {@hide} 668 */ 669 public native static final String getAssetAllocations(); 670 671 /** 672 * {@hide} 673 */ 674 public native static final int getGlobalAssetManagerCount(); 675 676 private native final int newTheme(); 677 private native final void deleteTheme(int theme); 678 /*package*/ native static final void applyThemeStyle(int theme, int styleRes, boolean force); 679 /*package*/ native static final void copyTheme(int dest, int source); 680 /*package*/ native static final int loadThemeAttributeValue(int theme, int ident, 681 TypedValue outValue, 682 boolean resolve); 683 /*package*/ native static final void dumpTheme(int theme, int priority, String tag, String prefix); 684 685 private native final int openXmlAssetNative(int cookie, String fileName); 686 687 private native final String[] getArrayStringResource(int arrayRes); 688 private native final int[] getArrayStringInfo(int arrayRes); 689 /*package*/ native final int[] getArrayIntResource(int arrayRes); 690 691 private native final void init(); 692 private native final void destroy(); 693 694 private final void decRefsLocked() { 695 mNumRefs--; 696 //System.out.println("Dec streams: mNumRefs=" + mNumRefs 697 // + " mReleased=" + mReleased); 698 if (mNumRefs == 0) { 699 destroy(); 700 } 701 } 702} 703