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