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