Home | History | Annotate | Download | only in jni
      1 /* //device/libs/android_runtime/android_util_AssetManager.cpp
      2 **
      3 ** Copyright 2006, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 
     18 #define LOG_TAG "asset"
     19 
     20 #define DEBUG_STYLES(x) //x
     21 #define THROW_ON_BAD_ID 0
     22 
     23 #include <android_runtime/android_util_AssetManager.h>
     24 
     25 #include "jni.h"
     26 #include "JNIHelp.h"
     27 #include "ScopedStringChars.h"
     28 #include "ScopedUtfChars.h"
     29 #include "android_util_Binder.h"
     30 #include <utils/misc.h>
     31 #include <android_runtime/AndroidRuntime.h>
     32 #include <utils/Log.h>
     33 
     34 #include <androidfw/Asset.h>
     35 #include <androidfw/AssetManager.h>
     36 #include <androidfw/ResourceTypes.h>
     37 
     38 #include <stdio.h>
     39 
     40 namespace android {
     41 
     42 // ----------------------------------------------------------------------------
     43 
     44 static struct typedvalue_offsets_t
     45 {
     46     jfieldID mType;
     47     jfieldID mData;
     48     jfieldID mString;
     49     jfieldID mAssetCookie;
     50     jfieldID mResourceId;
     51     jfieldID mChangingConfigurations;
     52     jfieldID mDensity;
     53 } gTypedValueOffsets;
     54 
     55 static struct assetfiledescriptor_offsets_t
     56 {
     57     jfieldID mFd;
     58     jfieldID mStartOffset;
     59     jfieldID mLength;
     60 } gAssetFileDescriptorOffsets;
     61 
     62 static struct assetmanager_offsets_t
     63 {
     64     jfieldID mObject;
     65 } gAssetManagerOffsets;
     66 
     67 jclass g_stringClass = NULL;
     68 
     69 // ----------------------------------------------------------------------------
     70 
     71 enum {
     72     STYLE_NUM_ENTRIES = 6,
     73     STYLE_TYPE = 0,
     74     STYLE_DATA = 1,
     75     STYLE_ASSET_COOKIE = 2,
     76     STYLE_RESOURCE_ID = 3,
     77     STYLE_CHANGING_CONFIGURATIONS = 4,
     78     STYLE_DENSITY = 5
     79 };
     80 
     81 static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
     82                       const Res_value& value, uint32_t ref, ssize_t block,
     83                       uint32_t typeSpecFlags, ResTable_config* config = NULL);
     84 
     85 jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
     86                const Res_value& value, uint32_t ref, ssize_t block,
     87                uint32_t typeSpecFlags, ResTable_config* config)
     88 {
     89     env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType);
     90     env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie,
     91                         (jint)table->getTableCookie(block));
     92     env->SetIntField(outValue, gTypedValueOffsets.mData, value.data);
     93     env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL);
     94     env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref);
     95     env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations,
     96             typeSpecFlags);
     97     if (config != NULL) {
     98         env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density);
     99     }
    100     return block;
    101 }
    102 
    103 // ----------------------------------------------------------------------------
    104 
    105 // this guy is exported to other jni routines
    106 AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj)
    107 {
    108     AssetManager* am = (AssetManager*)env->GetIntField(obj, gAssetManagerOffsets.mObject);
    109     if (am != NULL) {
    110         return am;
    111     }
    112     jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
    113     return NULL;
    114 }
    115 
    116 static jint android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz,
    117                                                 jstring fileName, jint mode)
    118 {
    119     AssetManager* am = assetManagerForJavaObject(env, clazz);
    120     if (am == NULL) {
    121         return 0;
    122     }
    123 
    124     ALOGV("openAsset in %p (Java object %p)\n", am, clazz);
    125 
    126     ScopedUtfChars fileName8(env, fileName);
    127     if (fileName8.c_str() == NULL) {
    128         return -1;
    129     }
    130 
    131     if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
    132         && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
    133         jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
    134         return -1;
    135     }
    136 
    137     Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode);
    138 
    139     if (a == NULL) {
    140         jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
    141         return -1;
    142     }
    143 
    144     //printf("Created Asset Stream: %p\n", a);
    145 
    146     return (jint)a;
    147 }
    148 
    149 static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets)
    150 {
    151     off64_t startOffset, length;
    152     int fd = a->openFileDescriptor(&startOffset, &length);
    153     delete a;
    154 
    155     if (fd < 0) {
    156         jniThrowException(env, "java/io/FileNotFoundException",
    157                 "This file can not be opened as a file descriptor; it is probably compressed");
    158         return NULL;
    159     }
    160 
    161     jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0);
    162     if (offsets == NULL) {
    163         close(fd);
    164         return NULL;
    165     }
    166 
    167     offsets[0] = startOffset;
    168     offsets[1] = length;
    169 
    170     env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0);
    171 
    172     jobject fileDesc = jniCreateFileDescriptor(env, fd);
    173     if (fileDesc == NULL) {
    174         close(fd);
    175         return NULL;
    176     }
    177 
    178     return newParcelFileDescriptor(env, fileDesc);
    179 }
    180 
    181 static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz,
    182                                                 jstring fileName, jlongArray outOffsets)
    183 {
    184     AssetManager* am = assetManagerForJavaObject(env, clazz);
    185     if (am == NULL) {
    186         return NULL;
    187     }
    188 
    189     ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz);
    190 
    191     ScopedUtfChars fileName8(env, fileName);
    192     if (fileName8.c_str() == NULL) {
    193         return NULL;
    194     }
    195 
    196     Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM);
    197 
    198     if (a == NULL) {
    199         jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
    200         return NULL;
    201     }
    202 
    203     //printf("Created Asset Stream: %p\n", a);
    204 
    205     return returnParcelFileDescriptor(env, a, outOffsets);
    206 }
    207 
    208 static jint android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz,
    209                                                          jint cookie,
    210                                                          jstring fileName,
    211                                                          jint mode)
    212 {
    213     AssetManager* am = assetManagerForJavaObject(env, clazz);
    214     if (am == NULL) {
    215         return 0;
    216     }
    217 
    218     ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz);
    219 
    220     ScopedUtfChars fileName8(env, fileName);
    221     if (fileName8.c_str() == NULL) {
    222         return -1;
    223     }
    224 
    225     if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
    226         && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
    227         jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
    228         return -1;
    229     }
    230 
    231     Asset* a = cookie
    232         ? am->openNonAsset((void*)cookie, fileName8.c_str(), (Asset::AccessMode)mode)
    233         : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode);
    234 
    235     if (a == NULL) {
    236         jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
    237         return -1;
    238     }
    239 
    240     //printf("Created Asset Stream: %p\n", a);
    241 
    242     return (jint)a;
    243 }
    244 
    245 static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz,
    246                                                          jint cookie,
    247                                                          jstring fileName,
    248                                                          jlongArray outOffsets)
    249 {
    250     AssetManager* am = assetManagerForJavaObject(env, clazz);
    251     if (am == NULL) {
    252         return NULL;
    253     }
    254 
    255     ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz);
    256 
    257     ScopedUtfChars fileName8(env, fileName);
    258     if (fileName8.c_str() == NULL) {
    259         return NULL;
    260     }
    261 
    262     Asset* a = cookie
    263         ? am->openNonAsset((void*)cookie, fileName8.c_str(), Asset::ACCESS_RANDOM)
    264         : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM);
    265 
    266     if (a == NULL) {
    267         jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
    268         return NULL;
    269     }
    270 
    271     //printf("Created Asset Stream: %p\n", a);
    272 
    273     return returnParcelFileDescriptor(env, a, outOffsets);
    274 }
    275 
    276 static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz,
    277                                                    jstring fileName)
    278 {
    279     AssetManager* am = assetManagerForJavaObject(env, clazz);
    280     if (am == NULL) {
    281         return NULL;
    282     }
    283 
    284     ScopedUtfChars fileName8(env, fileName);
    285     if (fileName8.c_str() == NULL) {
    286         return NULL;
    287     }
    288 
    289     AssetDir* dir = am->openDir(fileName8.c_str());
    290 
    291     if (dir == NULL) {
    292         jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
    293         return NULL;
    294     }
    295 
    296     size_t N = dir->getFileCount();
    297 
    298     jobjectArray array = env->NewObjectArray(dir->getFileCount(),
    299                                                 g_stringClass, NULL);
    300     if (array == NULL) {
    301         delete dir;
    302         return NULL;
    303     }
    304 
    305     for (size_t i=0; i<N; i++) {
    306         const String8& name = dir->getFileName(i);
    307         jstring str = env->NewStringUTF(name.string());
    308         if (str == NULL) {
    309             delete dir;
    310             return NULL;
    311         }
    312         env->SetObjectArrayElement(array, i, str);
    313         env->DeleteLocalRef(str);
    314     }
    315 
    316     delete dir;
    317 
    318     return array;
    319 }
    320 
    321 static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz,
    322                                                    jint asset)
    323 {
    324     Asset* a = (Asset*)asset;
    325 
    326     //printf("Destroying Asset Stream: %p\n", a);
    327 
    328     if (a == NULL) {
    329         jniThrowNullPointerException(env, "asset");
    330         return;
    331     }
    332 
    333     delete a;
    334 }
    335 
    336 static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz,
    337                                                     jint asset)
    338 {
    339     Asset* a = (Asset*)asset;
    340 
    341     if (a == NULL) {
    342         jniThrowNullPointerException(env, "asset");
    343         return -1;
    344     }
    345 
    346     uint8_t b;
    347     ssize_t res = a->read(&b, 1);
    348     return res == 1 ? b : -1;
    349 }
    350 
    351 static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz,
    352                                                 jint asset, jbyteArray bArray,
    353                                                 jint off, jint len)
    354 {
    355     Asset* a = (Asset*)asset;
    356 
    357     if (a == NULL || bArray == NULL) {
    358         jniThrowNullPointerException(env, "asset");
    359         return -1;
    360     }
    361 
    362     if (len == 0) {
    363         return 0;
    364     }
    365 
    366     jsize bLen = env->GetArrayLength(bArray);
    367     if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) {
    368         jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
    369         return -1;
    370     }
    371 
    372     jbyte* b = env->GetByteArrayElements(bArray, NULL);
    373     ssize_t res = a->read(b+off, len);
    374     env->ReleaseByteArrayElements(bArray, b, 0);
    375 
    376     if (res > 0) return res;
    377 
    378     if (res < 0) {
    379         jniThrowException(env, "java/io/IOException", "");
    380     }
    381     return -1;
    382 }
    383 
    384 static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz,
    385                                                  jint asset,
    386                                                  jlong offset, jint whence)
    387 {
    388     Asset* a = (Asset*)asset;
    389 
    390     if (a == NULL) {
    391         jniThrowNullPointerException(env, "asset");
    392         return -1;
    393     }
    394 
    395     return a->seek(
    396         offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR));
    397 }
    398 
    399 static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz,
    400                                                       jint asset)
    401 {
    402     Asset* a = (Asset*)asset;
    403 
    404     if (a == NULL) {
    405         jniThrowNullPointerException(env, "asset");
    406         return -1;
    407     }
    408 
    409     return a->getLength();
    410 }
    411 
    412 static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz,
    413                                                                jint asset)
    414 {
    415     Asset* a = (Asset*)asset;
    416 
    417     if (a == NULL) {
    418         jniThrowNullPointerException(env, "asset");
    419         return -1;
    420     }
    421 
    422     return a->getRemainingLength();
    423 }
    424 
    425 static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,
    426                                                        jstring path)
    427 {
    428     ScopedUtfChars path8(env, path);
    429     if (path8.c_str() == NULL) {
    430         return 0;
    431     }
    432 
    433     AssetManager* am = assetManagerForJavaObject(env, clazz);
    434     if (am == NULL) {
    435         return 0;
    436     }
    437 
    438     void* cookie;
    439     bool res = am->addAssetPath(String8(path8.c_str()), &cookie);
    440 
    441     return (res) ? (jint)cookie : 0;
    442 }
    443 
    444 static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz)
    445 {
    446     AssetManager* am = assetManagerForJavaObject(env, clazz);
    447     if (am == NULL) {
    448         return JNI_TRUE;
    449     }
    450     return am->isUpToDate() ? JNI_TRUE : JNI_FALSE;
    451 }
    452 
    453 static void android_content_AssetManager_setLocale(JNIEnv* env, jobject clazz,
    454                                                 jstring locale)
    455 {
    456     ScopedUtfChars locale8(env, locale);
    457     if (locale8.c_str() == NULL) {
    458         return;
    459     }
    460 
    461     AssetManager* am = assetManagerForJavaObject(env, clazz);
    462     if (am == NULL) {
    463         return;
    464     }
    465 
    466     am->setLocale(locale8.c_str());
    467 }
    468 
    469 static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
    470 {
    471     Vector<String8> locales;
    472 
    473     AssetManager* am = assetManagerForJavaObject(env, clazz);
    474     if (am == NULL) {
    475         return NULL;
    476     }
    477 
    478     am->getLocales(&locales);
    479 
    480     const int N = locales.size();
    481 
    482     jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL);
    483     if (result == NULL) {
    484         return NULL;
    485     }
    486 
    487     for (int i=0; i<N; i++) {
    488         jstring str = env->NewStringUTF(locales[i].string());
    489         if (str == NULL) {
    490             return NULL;
    491         }
    492         env->SetObjectArrayElement(result, i, str);
    493         env->DeleteLocalRef(str);
    494     }
    495 
    496     return result;
    497 }
    498 
    499 static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz,
    500                                                           jint mcc, jint mnc,
    501                                                           jstring locale, jint orientation,
    502                                                           jint touchscreen, jint density,
    503                                                           jint keyboard, jint keyboardHidden,
    504                                                           jint navigation,
    505                                                           jint screenWidth, jint screenHeight,
    506                                                           jint smallestScreenWidthDp,
    507                                                           jint screenWidthDp, jint screenHeightDp,
    508                                                           jint screenLayout, jint uiMode,
    509                                                           jint sdkVersion)
    510 {
    511     AssetManager* am = assetManagerForJavaObject(env, clazz);
    512     if (am == NULL) {
    513         return;
    514     }
    515 
    516     ResTable_config config;
    517     memset(&config, 0, sizeof(config));
    518 
    519     const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL;
    520 
    521     config.mcc = (uint16_t)mcc;
    522     config.mnc = (uint16_t)mnc;
    523     config.orientation = (uint8_t)orientation;
    524     config.touchscreen = (uint8_t)touchscreen;
    525     config.density = (uint16_t)density;
    526     config.keyboard = (uint8_t)keyboard;
    527     config.inputFlags = (uint8_t)keyboardHidden;
    528     config.navigation = (uint8_t)navigation;
    529     config.screenWidth = (uint16_t)screenWidth;
    530     config.screenHeight = (uint16_t)screenHeight;
    531     config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp;
    532     config.screenWidthDp = (uint16_t)screenWidthDp;
    533     config.screenHeightDp = (uint16_t)screenHeightDp;
    534     config.screenLayout = (uint8_t)screenLayout;
    535     config.uiMode = (uint8_t)uiMode;
    536     config.sdkVersion = (uint16_t)sdkVersion;
    537     config.minorVersion = 0;
    538     am->setConfiguration(config, locale8);
    539 
    540     if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8);
    541 }
    542 
    543 static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz,
    544                                                             jstring name,
    545                                                             jstring defType,
    546                                                             jstring defPackage)
    547 {
    548     ScopedStringChars name16(env, name);
    549     if (name16.get() == NULL) {
    550         return 0;
    551     }
    552 
    553     AssetManager* am = assetManagerForJavaObject(env, clazz);
    554     if (am == NULL) {
    555         return 0;
    556     }
    557 
    558     const char16_t* defType16 = defType
    559         ? env->GetStringChars(defType, NULL) : NULL;
    560     jsize defTypeLen = defType
    561         ? env->GetStringLength(defType) : 0;
    562     const char16_t* defPackage16 = defPackage
    563         ? env->GetStringChars(defPackage, NULL) : NULL;
    564     jsize defPackageLen = defPackage
    565         ? env->GetStringLength(defPackage) : 0;
    566 
    567     jint ident = am->getResources().identifierForName(
    568         name16.get(), name16.size(), defType16, defTypeLen, defPackage16, defPackageLen);
    569 
    570     if (defPackage16) {
    571         env->ReleaseStringChars(defPackage, defPackage16);
    572     }
    573     if (defType16) {
    574         env->ReleaseStringChars(defType, defType16);
    575     }
    576 
    577     return ident;
    578 }
    579 
    580 static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz,
    581                                                             jint resid)
    582 {
    583     AssetManager* am = assetManagerForJavaObject(env, clazz);
    584     if (am == NULL) {
    585         return NULL;
    586     }
    587 
    588     ResTable::resource_name name;
    589     if (!am->getResources().getResourceName(resid, true, &name)) {
    590         return NULL;
    591     }
    592 
    593     String16 str;
    594     if (name.package != NULL) {
    595         str.setTo(name.package, name.packageLen);
    596     }
    597     if (name.type8 != NULL || name.type != NULL) {
    598         if (str.size() > 0) {
    599             char16_t div = ':';
    600             str.append(&div, 1);
    601         }
    602         if (name.type8 != NULL) {
    603             str.append(String16(name.type8, name.typeLen));
    604         } else {
    605             str.append(name.type, name.typeLen);
    606         }
    607     }
    608     if (name.name8 != NULL || name.name != NULL) {
    609         if (str.size() > 0) {
    610             char16_t div = '/';
    611             str.append(&div, 1);
    612         }
    613         if (name.name8 != NULL) {
    614             str.append(String16(name.name8, name.nameLen));
    615         } else {
    616             str.append(name.name, name.nameLen);
    617         }
    618     }
    619 
    620     return env->NewString((const jchar*)str.string(), str.size());
    621 }
    622 
    623 static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz,
    624                                                                    jint resid)
    625 {
    626     AssetManager* am = assetManagerForJavaObject(env, clazz);
    627     if (am == NULL) {
    628         return NULL;
    629     }
    630 
    631     ResTable::resource_name name;
    632     if (!am->getResources().getResourceName(resid, true, &name)) {
    633         return NULL;
    634     }
    635 
    636     if (name.package != NULL) {
    637         return env->NewString((const jchar*)name.package, name.packageLen);
    638     }
    639 
    640     return NULL;
    641 }
    642 
    643 static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz,
    644                                                                 jint resid)
    645 {
    646     AssetManager* am = assetManagerForJavaObject(env, clazz);
    647     if (am == NULL) {
    648         return NULL;
    649     }
    650 
    651     ResTable::resource_name name;
    652     if (!am->getResources().getResourceName(resid, true, &name)) {
    653         return NULL;
    654     }
    655 
    656     if (name.type8 != NULL) {
    657         return env->NewStringUTF(name.type8);
    658     }
    659 
    660     if (name.type != NULL) {
    661         return env->NewString((const jchar*)name.type, name.typeLen);
    662     }
    663 
    664     return NULL;
    665 }
    666 
    667 static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz,
    668                                                                  jint resid)
    669 {
    670     AssetManager* am = assetManagerForJavaObject(env, clazz);
    671     if (am == NULL) {
    672         return NULL;
    673     }
    674 
    675     ResTable::resource_name name;
    676     if (!am->getResources().getResourceName(resid, true, &name)) {
    677         return NULL;
    678     }
    679 
    680     if (name.name8 != NULL) {
    681         return env->NewStringUTF(name.name8);
    682     }
    683 
    684     if (name.name != NULL) {
    685         return env->NewString((const jchar*)name.name, name.nameLen);
    686     }
    687 
    688     return NULL;
    689 }
    690 
    691 static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz,
    692                                                            jint ident,
    693                                                            jshort density,
    694                                                            jobject outValue,
    695                                                            jboolean resolve)
    696 {
    697     if (outValue == NULL) {
    698          jniThrowNullPointerException(env, "outValue");
    699          return 0;
    700     }
    701     AssetManager* am = assetManagerForJavaObject(env, clazz);
    702     if (am == NULL) {
    703         return 0;
    704     }
    705     const ResTable& res(am->getResources());
    706 
    707     Res_value value;
    708     ResTable_config config;
    709     uint32_t typeSpecFlags;
    710     ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config);
    711 #if THROW_ON_BAD_ID
    712     if (block == BAD_INDEX) {
    713         jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
    714         return 0;
    715     }
    716 #endif
    717     uint32_t ref = ident;
    718     if (resolve) {
    719         block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config);
    720 #if THROW_ON_BAD_ID
    721         if (block == BAD_INDEX) {
    722             jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
    723             return 0;
    724         }
    725 #endif
    726     }
    727     return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config) : block;
    728 }
    729 
    730 static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz,
    731                                                            jint ident, jint bagEntryId,
    732                                                            jobject outValue, jboolean resolve)
    733 {
    734     AssetManager* am = assetManagerForJavaObject(env, clazz);
    735     if (am == NULL) {
    736         return 0;
    737     }
    738     const ResTable& res(am->getResources());
    739 
    740     // Now lock down the resource object and start pulling stuff from it.
    741     res.lock();
    742 
    743     ssize_t block = -1;
    744     Res_value value;
    745 
    746     const ResTable::bag_entry* entry = NULL;
    747     uint32_t typeSpecFlags;
    748     ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags);
    749 
    750     for (ssize_t i=0; i<entryCount; i++) {
    751         if (((uint32_t)bagEntryId) == entry->map.name.ident) {
    752             block = entry->stringBlock;
    753             value = entry->map.value;
    754         }
    755         entry++;
    756     }
    757 
    758     res.unlock();
    759 
    760     if (block < 0) {
    761         return block;
    762     }
    763 
    764     uint32_t ref = ident;
    765     if (resolve) {
    766         block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
    767 #if THROW_ON_BAD_ID
    768         if (block == BAD_INDEX) {
    769             jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
    770             return 0;
    771         }
    772 #endif
    773     }
    774     return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block;
    775 }
    776 
    777 static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz)
    778 {
    779     AssetManager* am = assetManagerForJavaObject(env, clazz);
    780     if (am == NULL) {
    781         return 0;
    782     }
    783     return am->getResources().getTableCount();
    784 }
    785 
    786 static jint android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz,
    787                                                            jint block)
    788 {
    789     AssetManager* am = assetManagerForJavaObject(env, clazz);
    790     if (am == NULL) {
    791         return 0;
    792     }
    793     return (jint)am->getResources().getTableStringBlock(block);
    794 }
    795 
    796 static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz,
    797                                                        jint cookie)
    798 {
    799     AssetManager* am = assetManagerForJavaObject(env, clazz);
    800     if (am == NULL) {
    801         return NULL;
    802     }
    803     String8 name(am->getAssetPath((void*)cookie));
    804     if (name.length() == 0) {
    805         jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name");
    806         return NULL;
    807     }
    808     jstring str = env->NewStringUTF(name.string());
    809     return str;
    810 }
    811 
    812 static jint android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz)
    813 {
    814     AssetManager* am = assetManagerForJavaObject(env, clazz);
    815     if (am == NULL) {
    816         return 0;
    817     }
    818     return (jint)(new ResTable::Theme(am->getResources()));
    819 }
    820 
    821 static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz,
    822                                                      jint themeInt)
    823 {
    824     ResTable::Theme* theme = (ResTable::Theme*)themeInt;
    825     delete theme;
    826 }
    827 
    828 static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz,
    829                                                          jint themeInt,
    830                                                          jint styleRes,
    831                                                          jboolean force)
    832 {
    833     ResTable::Theme* theme = (ResTable::Theme*)themeInt;
    834     theme->applyStyle(styleRes, force ? true : false);
    835 }
    836 
    837 static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz,
    838                                                    jint destInt, jint srcInt)
    839 {
    840     ResTable::Theme* dest = (ResTable::Theme*)destInt;
    841     ResTable::Theme* src = (ResTable::Theme*)srcInt;
    842     dest->setTo(*src);
    843 }
    844 
    845 static jint android_content_AssetManager_loadThemeAttributeValue(
    846     JNIEnv* env, jobject clazz, jint themeInt, jint ident, jobject outValue, jboolean resolve)
    847 {
    848     ResTable::Theme* theme = (ResTable::Theme*)themeInt;
    849     const ResTable& res(theme->getResTable());
    850 
    851     Res_value value;
    852     // XXX value could be different in different configs!
    853     uint32_t typeSpecFlags = 0;
    854     ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags);
    855     uint32_t ref = 0;
    856     if (resolve) {
    857         block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
    858 #if THROW_ON_BAD_ID
    859         if (block == BAD_INDEX) {
    860             jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
    861             return 0;
    862         }
    863 #endif
    864     }
    865     return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block;
    866 }
    867 
    868 static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz,
    869                                                    jint themeInt, jint pri,
    870                                                    jstring tag, jstring prefix)
    871 {
    872     ResTable::Theme* theme = (ResTable::Theme*)themeInt;
    873     const ResTable& res(theme->getResTable());
    874 
    875     // XXX Need to use params.
    876     theme->dumpToLog();
    877 }
    878 
    879 static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject clazz,
    880                                                         jint themeToken,
    881                                                         jint defStyleAttr,
    882                                                         jint defStyleRes,
    883                                                         jint xmlParserToken,
    884                                                         jintArray attrs,
    885                                                         jintArray outValues,
    886                                                         jintArray outIndices)
    887 {
    888     if (themeToken == 0) {
    889         jniThrowNullPointerException(env, "theme token");
    890         return JNI_FALSE;
    891     }
    892     if (attrs == NULL) {
    893         jniThrowNullPointerException(env, "attrs");
    894         return JNI_FALSE;
    895     }
    896     if (outValues == NULL) {
    897         jniThrowNullPointerException(env, "out values");
    898         return JNI_FALSE;
    899     }
    900 
    901     DEBUG_STYLES(LOGI("APPLY STYLE: theme=0x%x defStyleAttr=0x%x defStyleRes=0x%x xml=0x%x",
    902         themeToken, defStyleAttr, defStyleRes, xmlParserToken));
    903 
    904     ResTable::Theme* theme = (ResTable::Theme*)themeToken;
    905     const ResTable& res = theme->getResTable();
    906     ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
    907     ResTable_config config;
    908     Res_value value;
    909 
    910     const jsize NI = env->GetArrayLength(attrs);
    911     const jsize NV = env->GetArrayLength(outValues);
    912     if (NV < (NI*STYLE_NUM_ENTRIES)) {
    913         jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
    914         return JNI_FALSE;
    915     }
    916 
    917     jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
    918     if (src == NULL) {
    919         return JNI_FALSE;
    920     }
    921 
    922     jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
    923     jint* dest = baseDest;
    924     if (dest == NULL) {
    925         env->ReleasePrimitiveArrayCritical(attrs, src, 0);
    926         return JNI_FALSE;
    927     }
    928 
    929     jint* indices = NULL;
    930     int indicesIdx = 0;
    931     if (outIndices != NULL) {
    932         if (env->GetArrayLength(outIndices) > NI) {
    933             indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
    934         }
    935     }
    936 
    937     // Load default style from attribute, if specified...
    938     uint32_t defStyleBagTypeSetFlags = 0;
    939     if (defStyleAttr != 0) {
    940         Res_value value;
    941         if (theme->getAttribute(defStyleAttr, &value, &defStyleBagTypeSetFlags) >= 0) {
    942             if (value.dataType == Res_value::TYPE_REFERENCE) {
    943                 defStyleRes = value.data;
    944             }
    945         }
    946     }
    947 
    948     // Retrieve the style class associated with the current XML tag.
    949     int style = 0;
    950     uint32_t styleBagTypeSetFlags = 0;
    951     if (xmlParser != NULL) {
    952         ssize_t idx = xmlParser->indexOfStyle();
    953         if (idx >= 0 && xmlParser->getAttributeValue(idx, &value) >= 0) {
    954             if (value.dataType == value.TYPE_ATTRIBUTE) {
    955                 if (theme->getAttribute(value.data, &value, &styleBagTypeSetFlags) < 0) {
    956                     value.dataType = Res_value::TYPE_NULL;
    957                 }
    958             }
    959             if (value.dataType == value.TYPE_REFERENCE) {
    960                 style = value.data;
    961             }
    962         }
    963     }
    964 
    965     // Now lock down the resource object and start pulling stuff from it.
    966     res.lock();
    967 
    968     // Retrieve the default style bag, if requested.
    969     const ResTable::bag_entry* defStyleEnt = NULL;
    970     uint32_t defStyleTypeSetFlags = 0;
    971     ssize_t bagOff = defStyleRes != 0
    972             ? res.getBagLocked(defStyleRes, &defStyleEnt, &defStyleTypeSetFlags) : -1;
    973     defStyleTypeSetFlags |= defStyleBagTypeSetFlags;
    974     const ResTable::bag_entry* endDefStyleEnt = defStyleEnt +
    975         (bagOff >= 0 ? bagOff : 0);
    976 
    977     // Retrieve the style class bag, if requested.
    978     const ResTable::bag_entry* styleEnt = NULL;
    979     uint32_t styleTypeSetFlags = 0;
    980     bagOff = style != 0 ? res.getBagLocked(style, &styleEnt, &styleTypeSetFlags) : -1;
    981     styleTypeSetFlags |= styleBagTypeSetFlags;
    982     const ResTable::bag_entry* endStyleEnt = styleEnt +
    983         (bagOff >= 0 ? bagOff : 0);
    984 
    985     // Retrieve the XML attributes, if requested.
    986     const jsize NX = xmlParser ? xmlParser->getAttributeCount() : 0;
    987     jsize ix=0;
    988     uint32_t curXmlAttr = xmlParser ? xmlParser->getAttributeNameResID(ix) : 0;
    989 
    990     static const ssize_t kXmlBlock = 0x10000000;
    991 
    992     // Now iterate through all of the attributes that the client has requested,
    993     // filling in each with whatever data we can find.
    994     ssize_t block = 0;
    995     uint32_t typeSetFlags;
    996     for (jsize ii=0; ii<NI; ii++) {
    997         const uint32_t curIdent = (uint32_t)src[ii];
    998 
    999         DEBUG_STYLES(LOGI("RETRIEVING ATTR 0x%08x...", curIdent));
   1000 
   1001         // Try to find a value for this attribute...  we prioritize values
   1002         // coming from, first XML attributes, then XML style, then default
   1003         // style, and finally the theme.
   1004         value.dataType = Res_value::TYPE_NULL;
   1005         value.data = 0;
   1006         typeSetFlags = 0;
   1007         config.density = 0;
   1008 
   1009         // Skip through XML attributes until the end or the next possible match.
   1010         while (ix < NX && curIdent > curXmlAttr) {
   1011             ix++;
   1012             curXmlAttr = xmlParser->getAttributeNameResID(ix);
   1013         }
   1014         // Retrieve the current XML attribute if it matches, and step to next.
   1015         if (ix < NX && curIdent == curXmlAttr) {
   1016             block = kXmlBlock;
   1017             xmlParser->getAttributeValue(ix, &value);
   1018             ix++;
   1019             curXmlAttr = xmlParser->getAttributeNameResID(ix);
   1020             DEBUG_STYLES(LOGI("-> From XML: type=0x%x, data=0x%08x",
   1021                     value.dataType, value.data));
   1022         }
   1023 
   1024         // Skip through the style values until the end or the next possible match.
   1025         while (styleEnt < endStyleEnt && curIdent > styleEnt->map.name.ident) {
   1026             styleEnt++;
   1027         }
   1028         // Retrieve the current style attribute if it matches, and step to next.
   1029         if (styleEnt < endStyleEnt && curIdent == styleEnt->map.name.ident) {
   1030             if (value.dataType == Res_value::TYPE_NULL) {
   1031                 block = styleEnt->stringBlock;
   1032                 typeSetFlags = styleTypeSetFlags;
   1033                 value = styleEnt->map.value;
   1034                 DEBUG_STYLES(LOGI("-> From style: type=0x%x, data=0x%08x",
   1035                         value.dataType, value.data));
   1036             }
   1037             styleEnt++;
   1038         }
   1039 
   1040         // Skip through the default style values until the end or the next possible match.
   1041         while (defStyleEnt < endDefStyleEnt && curIdent > defStyleEnt->map.name.ident) {
   1042             defStyleEnt++;
   1043         }
   1044         // Retrieve the current default style attribute if it matches, and step to next.
   1045         if (defStyleEnt < endDefStyleEnt && curIdent == defStyleEnt->map.name.ident) {
   1046             if (value.dataType == Res_value::TYPE_NULL) {
   1047                 block = defStyleEnt->stringBlock;
   1048                 typeSetFlags = defStyleTypeSetFlags;
   1049                 value = defStyleEnt->map.value;
   1050                 DEBUG_STYLES(LOGI("-> From def style: type=0x%x, data=0x%08x",
   1051                         value.dataType, value.data));
   1052             }
   1053             defStyleEnt++;
   1054         }
   1055 
   1056         uint32_t resid = 0;
   1057         if (value.dataType != Res_value::TYPE_NULL) {
   1058             // Take care of resolving the found resource to its final value.
   1059             ssize_t newBlock = theme->resolveAttributeReference(&value, block,
   1060                     &resid, &typeSetFlags, &config);
   1061             if (newBlock >= 0) block = newBlock;
   1062             DEBUG_STYLES(LOGI("-> Resolved attr: type=0x%x, data=0x%08x",
   1063                     value.dataType, value.data));
   1064         } else {
   1065             // If we still don't have a value for this attribute, try to find
   1066             // it in the theme!
   1067             ssize_t newBlock = theme->getAttribute(curIdent, &value, &typeSetFlags);
   1068             if (newBlock >= 0) {
   1069                 DEBUG_STYLES(LOGI("-> From theme: type=0x%x, data=0x%08x",
   1070                         value.dataType, value.data));
   1071                 newBlock = res.resolveReference(&value, block, &resid,
   1072                         &typeSetFlags, &config);
   1073 #if THROW_ON_BAD_ID
   1074                 if (newBlock == BAD_INDEX) {
   1075                     jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
   1076                     return JNI_FALSE;
   1077                 }
   1078 #endif
   1079                 if (newBlock >= 0) block = newBlock;
   1080                 DEBUG_STYLES(LOGI("-> Resolved theme: type=0x%x, data=0x%08x",
   1081                         value.dataType, value.data));
   1082             }
   1083         }
   1084 
   1085         // Deal with the special @null value -- it turns back to TYPE_NULL.
   1086         if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
   1087             DEBUG_STYLES(LOGI("-> Setting to @null!"));
   1088             value.dataType = Res_value::TYPE_NULL;
   1089             block = kXmlBlock;
   1090         }
   1091 
   1092         DEBUG_STYLES(LOGI("Attribute 0x%08x: type=0x%x, data=0x%08x",
   1093                 curIdent, value.dataType, value.data));
   1094 
   1095         // Write the final value back to Java.
   1096         dest[STYLE_TYPE] = value.dataType;
   1097         dest[STYLE_DATA] = value.data;
   1098         dest[STYLE_ASSET_COOKIE] =
   1099             block != kXmlBlock ? (jint)res.getTableCookie(block) : (jint)-1;
   1100         dest[STYLE_RESOURCE_ID] = resid;
   1101         dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
   1102         dest[STYLE_DENSITY] = config.density;
   1103 
   1104         if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
   1105             indicesIdx++;
   1106             indices[indicesIdx] = ii;
   1107         }
   1108 
   1109         dest += STYLE_NUM_ENTRIES;
   1110     }
   1111 
   1112     res.unlock();
   1113 
   1114     if (indices != NULL) {
   1115         indices[0] = indicesIdx;
   1116         env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
   1117     }
   1118     env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
   1119     env->ReleasePrimitiveArrayCritical(attrs, src, 0);
   1120 
   1121     return JNI_TRUE;
   1122 }
   1123 
   1124 static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz,
   1125                                                         jint xmlParserToken,
   1126                                                         jintArray attrs,
   1127                                                         jintArray outValues,
   1128                                                         jintArray outIndices)
   1129 {
   1130     if (xmlParserToken == 0) {
   1131         jniThrowNullPointerException(env, "xmlParserToken");
   1132         return JNI_FALSE;
   1133     }
   1134     if (attrs == NULL) {
   1135         jniThrowNullPointerException(env, "attrs");
   1136         return JNI_FALSE;
   1137     }
   1138     if (outValues == NULL) {
   1139         jniThrowNullPointerException(env, "out values");
   1140         return JNI_FALSE;
   1141     }
   1142 
   1143     AssetManager* am = assetManagerForJavaObject(env, clazz);
   1144     if (am == NULL) {
   1145         return JNI_FALSE;
   1146     }
   1147     const ResTable& res(am->getResources());
   1148     ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
   1149     ResTable_config config;
   1150     Res_value value;
   1151 
   1152     const jsize NI = env->GetArrayLength(attrs);
   1153     const jsize NV = env->GetArrayLength(outValues);
   1154     if (NV < (NI*STYLE_NUM_ENTRIES)) {
   1155         jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
   1156         return JNI_FALSE;
   1157     }
   1158 
   1159     jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
   1160     if (src == NULL) {
   1161         return JNI_FALSE;
   1162     }
   1163 
   1164     jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
   1165     jint* dest = baseDest;
   1166     if (dest == NULL) {
   1167         env->ReleasePrimitiveArrayCritical(attrs, src, 0);
   1168         return JNI_FALSE;
   1169     }
   1170 
   1171     jint* indices = NULL;
   1172     int indicesIdx = 0;
   1173     if (outIndices != NULL) {
   1174         if (env->GetArrayLength(outIndices) > NI) {
   1175             indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
   1176         }
   1177     }
   1178 
   1179     // Now lock down the resource object and start pulling stuff from it.
   1180     res.lock();
   1181 
   1182     // Retrieve the XML attributes, if requested.
   1183     const jsize NX = xmlParser->getAttributeCount();
   1184     jsize ix=0;
   1185     uint32_t curXmlAttr = xmlParser->getAttributeNameResID(ix);
   1186 
   1187     static const ssize_t kXmlBlock = 0x10000000;
   1188 
   1189     // Now iterate through all of the attributes that the client has requested,
   1190     // filling in each with whatever data we can find.
   1191     ssize_t block = 0;
   1192     uint32_t typeSetFlags;
   1193     for (jsize ii=0; ii<NI; ii++) {
   1194         const uint32_t curIdent = (uint32_t)src[ii];
   1195 
   1196         // Try to find a value for this attribute...
   1197         value.dataType = Res_value::TYPE_NULL;
   1198         value.data = 0;
   1199         typeSetFlags = 0;
   1200         config.density = 0;
   1201 
   1202         // Skip through XML attributes until the end or the next possible match.
   1203         while (ix < NX && curIdent > curXmlAttr) {
   1204             ix++;
   1205             curXmlAttr = xmlParser->getAttributeNameResID(ix);
   1206         }
   1207         // Retrieve the current XML attribute if it matches, and step to next.
   1208         if (ix < NX && curIdent == curXmlAttr) {
   1209             block = kXmlBlock;
   1210             xmlParser->getAttributeValue(ix, &value);
   1211             ix++;
   1212             curXmlAttr = xmlParser->getAttributeNameResID(ix);
   1213         }
   1214 
   1215         //printf("Attribute 0x%08x: type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
   1216         uint32_t resid = 0;
   1217         if (value.dataType != Res_value::TYPE_NULL) {
   1218             // Take care of resolving the found resource to its final value.
   1219             //printf("Resolving attribute reference\n");
   1220             ssize_t newBlock = res.resolveReference(&value, block, &resid,
   1221                     &typeSetFlags, &config);
   1222 #if THROW_ON_BAD_ID
   1223             if (newBlock == BAD_INDEX) {
   1224                 jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
   1225                 return JNI_FALSE;
   1226             }
   1227 #endif
   1228             if (newBlock >= 0) block = newBlock;
   1229         }
   1230 
   1231         // Deal with the special @null value -- it turns back to TYPE_NULL.
   1232         if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
   1233             value.dataType = Res_value::TYPE_NULL;
   1234         }
   1235 
   1236         //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
   1237 
   1238         // Write the final value back to Java.
   1239         dest[STYLE_TYPE] = value.dataType;
   1240         dest[STYLE_DATA] = value.data;
   1241         dest[STYLE_ASSET_COOKIE] =
   1242             block != kXmlBlock ? (jint)res.getTableCookie(block) : (jint)-1;
   1243         dest[STYLE_RESOURCE_ID] = resid;
   1244         dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
   1245         dest[STYLE_DENSITY] = config.density;
   1246 
   1247         if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
   1248             indicesIdx++;
   1249             indices[indicesIdx] = ii;
   1250         }
   1251 
   1252         dest += STYLE_NUM_ENTRIES;
   1253     }
   1254 
   1255     res.unlock();
   1256 
   1257     if (indices != NULL) {
   1258         indices[0] = indicesIdx;
   1259         env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
   1260     }
   1261 
   1262     env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
   1263     env->ReleasePrimitiveArrayCritical(attrs, src, 0);
   1264 
   1265     return JNI_TRUE;
   1266 }
   1267 
   1268 static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz,
   1269                                                        jint id)
   1270 {
   1271     AssetManager* am = assetManagerForJavaObject(env, clazz);
   1272     if (am == NULL) {
   1273         return 0;
   1274     }
   1275     const ResTable& res(am->getResources());
   1276 
   1277     res.lock();
   1278     const ResTable::bag_entry* defStyleEnt = NULL;
   1279     ssize_t bagOff = res.getBagLocked(id, &defStyleEnt);
   1280     res.unlock();
   1281 
   1282     return bagOff;
   1283 }
   1284 
   1285 static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz,
   1286                                                         jint id,
   1287                                                         jintArray outValues)
   1288 {
   1289     if (outValues == NULL) {
   1290         jniThrowNullPointerException(env, "out values");
   1291         return JNI_FALSE;
   1292     }
   1293 
   1294     AssetManager* am = assetManagerForJavaObject(env, clazz);
   1295     if (am == NULL) {
   1296         return JNI_FALSE;
   1297     }
   1298     const ResTable& res(am->getResources());
   1299     ResTable_config config;
   1300     Res_value value;
   1301     ssize_t block;
   1302 
   1303     const jsize NV = env->GetArrayLength(outValues);
   1304 
   1305     jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
   1306     jint* dest = baseDest;
   1307     if (dest == NULL) {
   1308         jniThrowException(env, "java/lang/OutOfMemoryError", "");
   1309         return JNI_FALSE;
   1310     }
   1311 
   1312     // Now lock down the resource object and start pulling stuff from it.
   1313     res.lock();
   1314 
   1315     const ResTable::bag_entry* arrayEnt = NULL;
   1316     uint32_t arrayTypeSetFlags = 0;
   1317     ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags);
   1318     const ResTable::bag_entry* endArrayEnt = arrayEnt +
   1319         (bagOff >= 0 ? bagOff : 0);
   1320 
   1321     int i = 0;
   1322     uint32_t typeSetFlags;
   1323     while (i < NV && arrayEnt < endArrayEnt) {
   1324         block = arrayEnt->stringBlock;
   1325         typeSetFlags = arrayTypeSetFlags;
   1326         config.density = 0;
   1327         value = arrayEnt->map.value;
   1328 
   1329         uint32_t resid = 0;
   1330         if (value.dataType != Res_value::TYPE_NULL) {
   1331             // Take care of resolving the found resource to its final value.
   1332             //printf("Resolving attribute reference\n");
   1333             ssize_t newBlock = res.resolveReference(&value, block, &resid,
   1334                     &typeSetFlags, &config);
   1335 #if THROW_ON_BAD_ID
   1336             if (newBlock == BAD_INDEX) {
   1337                 jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
   1338                 return JNI_FALSE;
   1339             }
   1340 #endif
   1341             if (newBlock >= 0) block = newBlock;
   1342         }
   1343 
   1344         // Deal with the special @null value -- it turns back to TYPE_NULL.
   1345         if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
   1346             value.dataType = Res_value::TYPE_NULL;
   1347         }
   1348 
   1349         //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
   1350 
   1351         // Write the final value back to Java.
   1352         dest[STYLE_TYPE] = value.dataType;
   1353         dest[STYLE_DATA] = value.data;
   1354         dest[STYLE_ASSET_COOKIE] = (jint)res.getTableCookie(block);
   1355         dest[STYLE_RESOURCE_ID] = resid;
   1356         dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
   1357         dest[STYLE_DENSITY] = config.density;
   1358         dest += STYLE_NUM_ENTRIES;
   1359         i+= STYLE_NUM_ENTRIES;
   1360         arrayEnt++;
   1361     }
   1362 
   1363     i /= STYLE_NUM_ENTRIES;
   1364 
   1365     res.unlock();
   1366 
   1367     env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
   1368 
   1369     return i;
   1370 }
   1371 
   1372 static jint android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz,
   1373                                                          jint cookie,
   1374                                                          jstring fileName)
   1375 {
   1376     AssetManager* am = assetManagerForJavaObject(env, clazz);
   1377     if (am == NULL) {
   1378         return 0;
   1379     }
   1380 
   1381     ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz);
   1382 
   1383     ScopedUtfChars fileName8(env, fileName);
   1384     if (fileName8.c_str() == NULL) {
   1385         return 0;
   1386     }
   1387 
   1388     Asset* a = cookie
   1389         ? am->openNonAsset((void*)cookie, fileName8.c_str(), Asset::ACCESS_BUFFER)
   1390         : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER);
   1391 
   1392     if (a == NULL) {
   1393         jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
   1394         return 0;
   1395     }
   1396 
   1397     ResXMLTree* block = new ResXMLTree();
   1398     status_t err = block->setTo(a->getBuffer(true), a->getLength(), true);
   1399     a->close();
   1400     delete a;
   1401 
   1402     if (err != NO_ERROR) {
   1403         jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
   1404         return 0;
   1405     }
   1406 
   1407     return (jint)block;
   1408 }
   1409 
   1410 static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz,
   1411                                                                  jint arrayResId)
   1412 {
   1413     AssetManager* am = assetManagerForJavaObject(env, clazz);
   1414     if (am == NULL) {
   1415         return NULL;
   1416     }
   1417     const ResTable& res(am->getResources());
   1418 
   1419     const ResTable::bag_entry* startOfBag;
   1420     const ssize_t N = res.lockBag(arrayResId, &startOfBag);
   1421     if (N < 0) {
   1422         return NULL;
   1423     }
   1424 
   1425     jintArray array = env->NewIntArray(N * 2);
   1426     if (array == NULL) {
   1427         res.unlockBag(startOfBag);
   1428         return NULL;
   1429     }
   1430 
   1431     Res_value value;
   1432     const ResTable::bag_entry* bag = startOfBag;
   1433     for (size_t i = 0, j = 0; ((ssize_t)i)<N; i++, bag++) {
   1434         jint stringIndex = -1;
   1435         jint stringBlock = 0;
   1436         value = bag->map.value;
   1437 
   1438         // Take care of resolving the found resource to its final value.
   1439         stringBlock = res.resolveReference(&value, bag->stringBlock, NULL);
   1440         if (value.dataType == Res_value::TYPE_STRING) {
   1441             stringIndex = value.data;
   1442         }
   1443 
   1444 #if THROW_ON_BAD_ID
   1445         if (stringBlock == BAD_INDEX) {
   1446             jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
   1447             return array;
   1448         }
   1449 #endif
   1450 
   1451         //todo: It might be faster to allocate a C array to contain
   1452         //      the blocknums and indices, put them in there and then
   1453         //      do just one SetIntArrayRegion()
   1454         env->SetIntArrayRegion(array, j, 1, &stringBlock);
   1455         env->SetIntArrayRegion(array, j + 1, 1, &stringIndex);
   1456         j = j + 2;
   1457     }
   1458     res.unlockBag(startOfBag);
   1459     return array;
   1460 }
   1461 
   1462 static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz,
   1463                                                                         jint arrayResId)
   1464 {
   1465     AssetManager* am = assetManagerForJavaObject(env, clazz);
   1466     if (am == NULL) {
   1467         return NULL;
   1468     }
   1469     const ResTable& res(am->getResources());
   1470 
   1471     const ResTable::bag_entry* startOfBag;
   1472     const ssize_t N = res.lockBag(arrayResId, &startOfBag);
   1473     if (N < 0) {
   1474         return NULL;
   1475     }
   1476 
   1477     jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL);
   1478     if (env->ExceptionCheck()) {
   1479         res.unlockBag(startOfBag);
   1480         return NULL;
   1481     }
   1482 
   1483     Res_value value;
   1484     const ResTable::bag_entry* bag = startOfBag;
   1485     size_t strLen = 0;
   1486     for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
   1487         value = bag->map.value;
   1488         jstring str = NULL;
   1489 
   1490         // Take care of resolving the found resource to its final value.
   1491         ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
   1492 #if THROW_ON_BAD_ID
   1493         if (block == BAD_INDEX) {
   1494             jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
   1495             return array;
   1496         }
   1497 #endif
   1498         if (value.dataType == Res_value::TYPE_STRING) {
   1499             const ResStringPool* pool = res.getTableStringBlock(block);
   1500             const char* str8 = pool->string8At(value.data, &strLen);
   1501             if (str8 != NULL) {
   1502                 str = env->NewStringUTF(str8);
   1503             } else {
   1504                 const char16_t* str16 = pool->stringAt(value.data, &strLen);
   1505                 str = env->NewString(str16, strLen);
   1506             }
   1507 
   1508             // If one of our NewString{UTF} calls failed due to memory, an
   1509             // exception will be pending.
   1510             if (env->ExceptionCheck()) {
   1511                 res.unlockBag(startOfBag);
   1512                 return NULL;
   1513             }
   1514 
   1515             env->SetObjectArrayElement(array, i, str);
   1516 
   1517             // str is not NULL at that point, otherwise ExceptionCheck would have been true.
   1518             // If we have a large amount of strings in our array, we might
   1519             // overflow the local reference table of the VM.
   1520             env->DeleteLocalRef(str);
   1521         }
   1522     }
   1523     res.unlockBag(startOfBag);
   1524     return array;
   1525 }
   1526 
   1527 static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz,
   1528                                                                         jint arrayResId)
   1529 {
   1530     AssetManager* am = assetManagerForJavaObject(env, clazz);
   1531     if (am == NULL) {
   1532         return NULL;
   1533     }
   1534     const ResTable& res(am->getResources());
   1535 
   1536     const ResTable::bag_entry* startOfBag;
   1537     const ssize_t N = res.lockBag(arrayResId, &startOfBag);
   1538     if (N < 0) {
   1539         return NULL;
   1540     }
   1541 
   1542     jintArray array = env->NewIntArray(N);
   1543     if (array == NULL) {
   1544         res.unlockBag(startOfBag);
   1545         return NULL;
   1546     }
   1547 
   1548     Res_value value;
   1549     const ResTable::bag_entry* bag = startOfBag;
   1550     for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
   1551         value = bag->map.value;
   1552 
   1553         // Take care of resolving the found resource to its final value.
   1554         ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
   1555 #if THROW_ON_BAD_ID
   1556         if (block == BAD_INDEX) {
   1557             jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
   1558             return array;
   1559         }
   1560 #endif
   1561         if (value.dataType >= Res_value::TYPE_FIRST_INT
   1562                 && value.dataType <= Res_value::TYPE_LAST_INT) {
   1563             int intVal = value.data;
   1564             env->SetIntArrayRegion(array, i, 1, &intVal);
   1565         }
   1566     }
   1567     res.unlockBag(startOfBag);
   1568     return array;
   1569 }
   1570 
   1571 static void android_content_AssetManager_init(JNIEnv* env, jobject clazz)
   1572 {
   1573     AssetManager* am = new AssetManager();
   1574     if (am == NULL) {
   1575         jniThrowException(env, "java/lang/OutOfMemoryError", "");
   1576         return;
   1577     }
   1578 
   1579     am->addDefaultAssets();
   1580 
   1581     ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
   1582     env->SetIntField(clazz, gAssetManagerOffsets.mObject, (jint)am);
   1583 }
   1584 
   1585 static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz)
   1586 {
   1587     AssetManager* am = (AssetManager*)
   1588         (env->GetIntField(clazz, gAssetManagerOffsets.mObject));
   1589     ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz);
   1590     if (am != NULL) {
   1591         delete am;
   1592         env->SetIntField(clazz, gAssetManagerOffsets.mObject, 0);
   1593     }
   1594 }
   1595 
   1596 static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz)
   1597 {
   1598     return Asset::getGlobalCount();
   1599 }
   1600 
   1601 static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz)
   1602 {
   1603     String8 alloc = Asset::getAssetAllocations();
   1604     if (alloc.length() <= 0) {
   1605         return NULL;
   1606     }
   1607 
   1608     jstring str = env->NewStringUTF(alloc.string());
   1609     return str;
   1610 }
   1611 
   1612 static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz)
   1613 {
   1614     return AssetManager::getGlobalCount();
   1615 }
   1616 
   1617 // ----------------------------------------------------------------------------
   1618 
   1619 /*
   1620  * JNI registration.
   1621  */
   1622 static JNINativeMethod gAssetManagerMethods[] = {
   1623     /* name, signature, funcPtr */
   1624 
   1625     // Basic asset stuff.
   1626     { "openAsset",      "(Ljava/lang/String;I)I",
   1627         (void*) android_content_AssetManager_openAsset },
   1628     { "openAssetFd",      "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
   1629         (void*) android_content_AssetManager_openAssetFd },
   1630     { "openNonAssetNative", "(ILjava/lang/String;I)I",
   1631         (void*) android_content_AssetManager_openNonAssetNative },
   1632     { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
   1633         (void*) android_content_AssetManager_openNonAssetFdNative },
   1634     { "list",           "(Ljava/lang/String;)[Ljava/lang/String;",
   1635         (void*) android_content_AssetManager_list },
   1636     { "destroyAsset",   "(I)V",
   1637         (void*) android_content_AssetManager_destroyAsset },
   1638     { "readAssetChar",  "(I)I",
   1639         (void*) android_content_AssetManager_readAssetChar },
   1640     { "readAsset",      "(I[BII)I",
   1641         (void*) android_content_AssetManager_readAsset },
   1642     { "seekAsset",      "(IJI)J",
   1643         (void*) android_content_AssetManager_seekAsset },
   1644     { "getAssetLength", "(I)J",
   1645         (void*) android_content_AssetManager_getAssetLength },
   1646     { "getAssetRemainingLength", "(I)J",
   1647         (void*) android_content_AssetManager_getAssetRemainingLength },
   1648     { "addAssetPathNative", "(Ljava/lang/String;)I",
   1649         (void*) android_content_AssetManager_addAssetPath },
   1650     { "isUpToDate",     "()Z",
   1651         (void*) android_content_AssetManager_isUpToDate },
   1652 
   1653     // Resources.
   1654     { "setLocale",      "(Ljava/lang/String;)V",
   1655         (void*) android_content_AssetManager_setLocale },
   1656     { "getLocales",      "()[Ljava/lang/String;",
   1657         (void*) android_content_AssetManager_getLocales },
   1658     { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIII)V",
   1659         (void*) android_content_AssetManager_setConfiguration },
   1660     { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
   1661         (void*) android_content_AssetManager_getResourceIdentifier },
   1662     { "getResourceName","(I)Ljava/lang/String;",
   1663         (void*) android_content_AssetManager_getResourceName },
   1664     { "getResourcePackageName","(I)Ljava/lang/String;",
   1665         (void*) android_content_AssetManager_getResourcePackageName },
   1666     { "getResourceTypeName","(I)Ljava/lang/String;",
   1667         (void*) android_content_AssetManager_getResourceTypeName },
   1668     { "getResourceEntryName","(I)Ljava/lang/String;",
   1669         (void*) android_content_AssetManager_getResourceEntryName },
   1670     { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I",
   1671         (void*) android_content_AssetManager_loadResourceValue },
   1672     { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I",
   1673         (void*) android_content_AssetManager_loadResourceBagValue },
   1674     { "getStringBlockCount","()I",
   1675         (void*) android_content_AssetManager_getStringBlockCount },
   1676     { "getNativeStringBlock","(I)I",
   1677         (void*) android_content_AssetManager_getNativeStringBlock },
   1678     { "getCookieName","(I)Ljava/lang/String;",
   1679         (void*) android_content_AssetManager_getCookieName },
   1680 
   1681     // Themes.
   1682     { "newTheme", "()I",
   1683         (void*) android_content_AssetManager_newTheme },
   1684     { "deleteTheme", "(I)V",
   1685         (void*) android_content_AssetManager_deleteTheme },
   1686     { "applyThemeStyle", "(IIZ)V",
   1687         (void*) android_content_AssetManager_applyThemeStyle },
   1688     { "copyTheme", "(II)V",
   1689         (void*) android_content_AssetManager_copyTheme },
   1690     { "loadThemeAttributeValue", "(IILandroid/util/TypedValue;Z)I",
   1691         (void*) android_content_AssetManager_loadThemeAttributeValue },
   1692     { "dumpTheme", "(IILjava/lang/String;Ljava/lang/String;)V",
   1693         (void*) android_content_AssetManager_dumpTheme },
   1694     { "applyStyle","(IIII[I[I[I)Z",
   1695         (void*) android_content_AssetManager_applyStyle },
   1696     { "retrieveAttributes","(I[I[I[I)Z",
   1697         (void*) android_content_AssetManager_retrieveAttributes },
   1698     { "getArraySize","(I)I",
   1699         (void*) android_content_AssetManager_getArraySize },
   1700     { "retrieveArray","(I[I)I",
   1701         (void*) android_content_AssetManager_retrieveArray },
   1702 
   1703     // XML files.
   1704     { "openXmlAssetNative", "(ILjava/lang/String;)I",
   1705         (void*) android_content_AssetManager_openXmlAssetNative },
   1706 
   1707     // Arrays.
   1708     { "getArrayStringResource","(I)[Ljava/lang/String;",
   1709         (void*) android_content_AssetManager_getArrayStringResource },
   1710     { "getArrayStringInfo","(I)[I",
   1711         (void*) android_content_AssetManager_getArrayStringInfo },
   1712     { "getArrayIntResource","(I)[I",
   1713         (void*) android_content_AssetManager_getArrayIntResource },
   1714 
   1715     // Bookkeeping.
   1716     { "init",           "()V",
   1717         (void*) android_content_AssetManager_init },
   1718     { "destroy",        "()V",
   1719         (void*) android_content_AssetManager_destroy },
   1720     { "getGlobalAssetCount", "()I",
   1721         (void*) android_content_AssetManager_getGlobalAssetCount },
   1722     { "getAssetAllocations", "()Ljava/lang/String;",
   1723         (void*) android_content_AssetManager_getAssetAllocations },
   1724     { "getGlobalAssetManagerCount", "()I",
   1725         (void*) android_content_AssetManager_getGlobalAssetCount },
   1726 };
   1727 
   1728 int register_android_content_AssetManager(JNIEnv* env)
   1729 {
   1730     jclass typedValue = env->FindClass("android/util/TypedValue");
   1731     LOG_FATAL_IF(typedValue == NULL, "Unable to find class android/util/TypedValue");
   1732     gTypedValueOffsets.mType
   1733         = env->GetFieldID(typedValue, "type", "I");
   1734     LOG_FATAL_IF(gTypedValueOffsets.mType == NULL, "Unable to find TypedValue.type");
   1735     gTypedValueOffsets.mData
   1736         = env->GetFieldID(typedValue, "data", "I");
   1737     LOG_FATAL_IF(gTypedValueOffsets.mData == NULL, "Unable to find TypedValue.data");
   1738     gTypedValueOffsets.mString
   1739         = env->GetFieldID(typedValue, "string", "Ljava/lang/CharSequence;");
   1740     LOG_FATAL_IF(gTypedValueOffsets.mString == NULL, "Unable to find TypedValue.string");
   1741     gTypedValueOffsets.mAssetCookie
   1742         = env->GetFieldID(typedValue, "assetCookie", "I");
   1743     LOG_FATAL_IF(gTypedValueOffsets.mAssetCookie == NULL, "Unable to find TypedValue.assetCookie");
   1744     gTypedValueOffsets.mResourceId
   1745         = env->GetFieldID(typedValue, "resourceId", "I");
   1746     LOG_FATAL_IF(gTypedValueOffsets.mResourceId == NULL, "Unable to find TypedValue.resourceId");
   1747     gTypedValueOffsets.mChangingConfigurations
   1748         = env->GetFieldID(typedValue, "changingConfigurations", "I");
   1749     LOG_FATAL_IF(gTypedValueOffsets.mChangingConfigurations == NULL, "Unable to find TypedValue.changingConfigurations");
   1750     gTypedValueOffsets.mDensity = env->GetFieldID(typedValue, "density", "I");
   1751     LOG_FATAL_IF(gTypedValueOffsets.mDensity == NULL, "Unable to find TypedValue.density");
   1752 
   1753     jclass assetFd = env->FindClass("android/content/res/AssetFileDescriptor");
   1754     LOG_FATAL_IF(assetFd == NULL, "Unable to find class android/content/res/AssetFileDescriptor");
   1755     gAssetFileDescriptorOffsets.mFd
   1756         = env->GetFieldID(assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
   1757     LOG_FATAL_IF(gAssetFileDescriptorOffsets.mFd == NULL, "Unable to find AssetFileDescriptor.mFd");
   1758     gAssetFileDescriptorOffsets.mStartOffset
   1759         = env->GetFieldID(assetFd, "mStartOffset", "J");
   1760     LOG_FATAL_IF(gAssetFileDescriptorOffsets.mStartOffset == NULL, "Unable to find AssetFileDescriptor.mStartOffset");
   1761     gAssetFileDescriptorOffsets.mLength
   1762         = env->GetFieldID(assetFd, "mLength", "J");
   1763     LOG_FATAL_IF(gAssetFileDescriptorOffsets.mLength == NULL, "Unable to find AssetFileDescriptor.mLength");
   1764 
   1765     jclass assetManager = env->FindClass("android/content/res/AssetManager");
   1766     LOG_FATAL_IF(assetManager == NULL, "Unable to find class android/content/res/AssetManager");
   1767     gAssetManagerOffsets.mObject
   1768         = env->GetFieldID(assetManager, "mObject", "I");
   1769     LOG_FATAL_IF(gAssetManagerOffsets.mObject == NULL, "Unable to find AssetManager.mObject");
   1770 
   1771     jclass stringClass = env->FindClass("java/lang/String");
   1772     LOG_FATAL_IF(stringClass == NULL, "Unable to find class java/lang/String");
   1773     g_stringClass = (jclass)env->NewGlobalRef(stringClass);
   1774     LOG_FATAL_IF(g_stringClass == NULL, "Unable to create global reference for class java/lang/String");
   1775 
   1776     return AndroidRuntime::registerNativeMethods(env,
   1777             "android/content/res/AssetManager", gAssetManagerMethods, NELEM(gAssetManagerMethods));
   1778 }
   1779 
   1780 }; // namespace android
   1781