android_util_AssetManager.cpp revision 69cb87576ba163b61bb0e6477a3b7c57a9b11d40
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
38namespace android {
39
40// ----------------------------------------------------------------------------
41
42static 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
53static struct assetfiledescriptor_offsets_t
54{
55    jfieldID mFd;
56    jfieldID mStartOffset;
57    jfieldID mLength;
58} gAssetFileDescriptorOffsets;
59
60static struct assetmanager_offsets_t
61{
62    jfieldID mObject;
63} gAssetManagerOffsets;
64
65jclass g_stringClass = NULL;
66
67// ----------------------------------------------------------------------------
68
69enum {
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
79static 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
83jint 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
104AssetManager* 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
114static 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
150static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets)
151{
152    off64_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
182static 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
212static 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
252static 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
286static 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
342static 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
357static 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
372static 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
405static 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
420static 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
433static 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
446static 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
469static 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
478static 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
498static 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
528static 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 smallestScreenWidthDp,
536                                                          jint screenWidthDp, jint screenHeightDp,
537                                                          jint screenLayout, jint uiMode,
538                                                          jint sdkVersion)
539{
540    AssetManager* am = assetManagerForJavaObject(env, clazz);
541    if (am == NULL) {
542        return;
543    }
544
545    ResTable_config config;
546    memset(&config, 0, sizeof(config));
547
548    const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL;
549
550    config.mcc = (uint16_t)mcc;
551    config.mnc = (uint16_t)mnc;
552    config.orientation = (uint8_t)orientation;
553    config.touchscreen = (uint8_t)touchscreen;
554    config.density = (uint16_t)density;
555    config.keyboard = (uint8_t)keyboard;
556    config.inputFlags = (uint8_t)keyboardHidden;
557    config.navigation = (uint8_t)navigation;
558    config.screenWidth = (uint16_t)screenWidth;
559    config.screenHeight = (uint16_t)screenHeight;
560    config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp;
561    config.screenWidthDp = (uint16_t)screenWidthDp;
562    config.screenHeightDp = (uint16_t)screenHeightDp;
563    config.screenLayout = (uint8_t)screenLayout;
564    config.uiMode = (uint8_t)uiMode;
565    config.sdkVersion = (uint16_t)sdkVersion;
566    config.minorVersion = 0;
567    am->setConfiguration(config, locale8);
568
569    if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8);
570}
571
572static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz,
573                                                            jstring name,
574                                                            jstring defType,
575                                                            jstring defPackage)
576{
577    if (name == NULL) {
578        jniThrowException(env, "java/lang/NullPointerException", "name");
579        return 0;
580    }
581
582    AssetManager* am = assetManagerForJavaObject(env, clazz);
583    if (am == NULL) {
584        return 0;
585    }
586
587    const char16_t* name16 = env->GetStringChars(name, NULL);
588    jsize nameLen = env->GetStringLength(name);
589    const char16_t* defType16 = defType
590        ? env->GetStringChars(defType, NULL) : NULL;
591    jsize defTypeLen = defType
592        ? env->GetStringLength(defType) : 0;
593    const char16_t* defPackage16 = defPackage
594        ? env->GetStringChars(defPackage, NULL) : NULL;
595    jsize defPackageLen = defPackage
596        ? env->GetStringLength(defPackage) : 0;
597
598    jint ident = am->getResources().identifierForName(
599        name16, nameLen, defType16, defTypeLen, defPackage16, defPackageLen);
600
601    if (defPackage16) {
602        env->ReleaseStringChars(defPackage, defPackage16);
603    }
604    if (defType16) {
605        env->ReleaseStringChars(defType, defType16);
606    }
607    env->ReleaseStringChars(name, name16);
608
609    return ident;
610}
611
612static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz,
613                                                            jint resid)
614{
615    AssetManager* am = assetManagerForJavaObject(env, clazz);
616    if (am == NULL) {
617        return NULL;
618    }
619
620    ResTable::resource_name name;
621    if (!am->getResources().getResourceName(resid, &name)) {
622        return NULL;
623    }
624
625    String16 str;
626    if (name.package != NULL) {
627        str.setTo(name.package, name.packageLen);
628    }
629    if (name.type != NULL) {
630        if (str.size() > 0) {
631            char16_t div = ':';
632            str.append(&div, 1);
633        }
634        str.append(name.type, name.typeLen);
635    }
636    if (name.name != NULL) {
637        if (str.size() > 0) {
638            char16_t div = '/';
639            str.append(&div, 1);
640        }
641        str.append(name.name, name.nameLen);
642    }
643
644    return env->NewString((const jchar*)str.string(), str.size());
645}
646
647static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz,
648                                                                   jint resid)
649{
650    AssetManager* am = assetManagerForJavaObject(env, clazz);
651    if (am == NULL) {
652        return NULL;
653    }
654
655    ResTable::resource_name name;
656    if (!am->getResources().getResourceName(resid, &name)) {
657        return NULL;
658    }
659
660    if (name.package != NULL) {
661        return env->NewString((const jchar*)name.package, name.packageLen);
662    }
663
664    return NULL;
665}
666
667static jstring android_content_AssetManager_getResourceTypeName(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, &name)) {
677        return NULL;
678    }
679
680    if (name.type != NULL) {
681        return env->NewString((const jchar*)name.type, name.typeLen);
682    }
683
684    return NULL;
685}
686
687static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz,
688                                                                 jint resid)
689{
690    AssetManager* am = assetManagerForJavaObject(env, clazz);
691    if (am == NULL) {
692        return NULL;
693    }
694
695    ResTable::resource_name name;
696    if (!am->getResources().getResourceName(resid, &name)) {
697        return NULL;
698    }
699
700    if (name.name != NULL) {
701        return env->NewString((const jchar*)name.name, name.nameLen);
702    }
703
704    return NULL;
705}
706
707static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz,
708                                                           jint ident,
709                                                           jshort density,
710                                                           jobject outValue,
711                                                           jboolean resolve)
712{
713    AssetManager* am = assetManagerForJavaObject(env, clazz);
714    if (am == NULL) {
715        return 0;
716    }
717    const ResTable& res(am->getResources());
718
719    Res_value value;
720    ResTable_config config;
721    uint32_t typeSpecFlags;
722    ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config);
723#if THROW_ON_BAD_ID
724    if (block == BAD_INDEX) {
725        jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
726        return 0;
727    }
728#endif
729    uint32_t ref = ident;
730    if (resolve) {
731        block = res.resolveReference(&value, block, &ref);
732#if THROW_ON_BAD_ID
733        if (block == BAD_INDEX) {
734            jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
735            return 0;
736        }
737#endif
738    }
739    return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config) : block;
740}
741
742static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz,
743                                                           jint ident, jint bagEntryId,
744                                                           jobject outValue, jboolean resolve)
745{
746    AssetManager* am = assetManagerForJavaObject(env, clazz);
747    if (am == NULL) {
748        return 0;
749    }
750    const ResTable& res(am->getResources());
751
752    // Now lock down the resource object and start pulling stuff from it.
753    res.lock();
754
755    ssize_t block = -1;
756    Res_value value;
757
758    const ResTable::bag_entry* entry = NULL;
759    uint32_t typeSpecFlags;
760    ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags);
761
762    for (ssize_t i=0; i<entryCount; i++) {
763        if (((uint32_t)bagEntryId) == entry->map.name.ident) {
764            block = entry->stringBlock;
765            value = entry->map.value;
766        }
767        entry++;
768    }
769
770    res.unlock();
771
772    if (block < 0) {
773        return block;
774    }
775
776    uint32_t ref = ident;
777    if (resolve) {
778        block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
779#if THROW_ON_BAD_ID
780        if (block == BAD_INDEX) {
781            jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
782            return 0;
783        }
784#endif
785    }
786    return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block;
787}
788
789static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz)
790{
791    AssetManager* am = assetManagerForJavaObject(env, clazz);
792    if (am == NULL) {
793        return 0;
794    }
795    return am->getResources().getTableCount();
796}
797
798static jint android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz,
799                                                           jint block)
800{
801    AssetManager* am = assetManagerForJavaObject(env, clazz);
802    if (am == NULL) {
803        return 0;
804    }
805    return (jint)am->getResources().getTableStringBlock(block);
806}
807
808static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz,
809                                                       jint cookie)
810{
811    AssetManager* am = assetManagerForJavaObject(env, clazz);
812    if (am == NULL) {
813        return NULL;
814    }
815    String8 name(am->getAssetPath((void*)cookie));
816    if (name.length() == 0) {
817        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name");
818        return NULL;
819    }
820    jstring str = env->NewStringUTF(name.string());
821    return str;
822}
823
824static jint android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz)
825{
826    AssetManager* am = assetManagerForJavaObject(env, clazz);
827    if (am == NULL) {
828        return 0;
829    }
830    return (jint)(new ResTable::Theme(am->getResources()));
831}
832
833static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz,
834                                                     jint themeInt)
835{
836    ResTable::Theme* theme = (ResTable::Theme*)themeInt;
837    delete theme;
838}
839
840static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz,
841                                                         jint themeInt,
842                                                         jint styleRes,
843                                                         jboolean force)
844{
845    ResTable::Theme* theme = (ResTable::Theme*)themeInt;
846    theme->applyStyle(styleRes, force ? true : false);
847}
848
849static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz,
850                                                   jint destInt, jint srcInt)
851{
852    ResTable::Theme* dest = (ResTable::Theme*)destInt;
853    ResTable::Theme* src = (ResTable::Theme*)srcInt;
854    dest->setTo(*src);
855}
856
857static jint android_content_AssetManager_loadThemeAttributeValue(
858    JNIEnv* env, jobject clazz, jint themeInt, jint ident, jobject outValue, jboolean resolve)
859{
860    ResTable::Theme* theme = (ResTable::Theme*)themeInt;
861    const ResTable& res(theme->getResTable());
862
863    Res_value value;
864    // XXX value could be different in different configs!
865    uint32_t typeSpecFlags = 0;
866    ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags);
867    uint32_t ref = 0;
868    if (resolve) {
869        block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
870#if THROW_ON_BAD_ID
871        if (block == BAD_INDEX) {
872            jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
873            return 0;
874        }
875#endif
876    }
877    return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block;
878}
879
880static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz,
881                                                   jint themeInt, jint pri,
882                                                   jstring tag, jstring prefix)
883{
884    ResTable::Theme* theme = (ResTable::Theme*)themeInt;
885    const ResTable& res(theme->getResTable());
886
887    if (tag == NULL) {
888        jniThrowException(env, "java/lang/NullPointerException", "tag");
889        return;
890    }
891
892    const char* tag8 = env->GetStringUTFChars(tag, NULL);
893    const char* prefix8 = NULL;
894    if (prefix != NULL) {
895        prefix8 = env->GetStringUTFChars(prefix, NULL);
896    }
897
898    // XXX Need to use params.
899    theme->dumpToLog();
900
901    if (prefix8 != NULL) {
902        env->ReleaseStringUTFChars(prefix, prefix8);
903    }
904    env->ReleaseStringUTFChars(tag, tag8);
905}
906
907static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject clazz,
908                                                        jint themeToken,
909                                                        jint defStyleAttr,
910                                                        jint defStyleRes,
911                                                        jint xmlParserToken,
912                                                        jintArray attrs,
913                                                        jintArray outValues,
914                                                        jintArray outIndices)
915{
916    if (themeToken == 0) {
917        jniThrowException(env, "java/lang/NullPointerException", "theme token");
918        return JNI_FALSE;
919    }
920    if (attrs == NULL) {
921        jniThrowException(env, "java/lang/NullPointerException", "attrs");
922        return JNI_FALSE;
923    }
924    if (outValues == NULL) {
925        jniThrowException(env, "java/lang/NullPointerException", "out values");
926        return JNI_FALSE;
927    }
928
929    DEBUG_STYLES(LOGI("APPLY STYLE: theme=0x%x defStyleAttr=0x%x defStyleRes=0x%x xml=0x%x",
930        themeToken, defStyleAttr, defStyleRes, xmlParserToken));
931
932    ResTable::Theme* theme = (ResTable::Theme*)themeToken;
933    const ResTable& res = theme->getResTable();
934    ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
935    ResTable_config config;
936    Res_value value;
937
938    const jsize NI = env->GetArrayLength(attrs);
939    const jsize NV = env->GetArrayLength(outValues);
940    if (NV < (NI*STYLE_NUM_ENTRIES)) {
941        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
942        return JNI_FALSE;
943    }
944
945    jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
946    if (src == NULL) {
947        jniThrowException(env, "java/lang/OutOfMemoryError", "");
948        return JNI_FALSE;
949    }
950
951    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
952    jint* dest = baseDest;
953    if (dest == NULL) {
954        env->ReleasePrimitiveArrayCritical(attrs, src, 0);
955        jniThrowException(env, "java/lang/OutOfMemoryError", "");
956        return JNI_FALSE;
957    }
958
959    jint* indices = NULL;
960    int indicesIdx = 0;
961    if (outIndices != NULL) {
962        if (env->GetArrayLength(outIndices) > NI) {
963            indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
964        }
965    }
966
967    // Load default style from attribute, if specified...
968    uint32_t defStyleBagTypeSetFlags = 0;
969    if (defStyleAttr != 0) {
970        Res_value value;
971        if (theme->getAttribute(defStyleAttr, &value, &defStyleBagTypeSetFlags) >= 0) {
972            if (value.dataType == Res_value::TYPE_REFERENCE) {
973                defStyleRes = value.data;
974            }
975        }
976    }
977
978    // Retrieve the style class associated with the current XML tag.
979    int style = 0;
980    uint32_t styleBagTypeSetFlags = 0;
981    if (xmlParser != NULL) {
982        ssize_t idx = xmlParser->indexOfStyle();
983        if (idx >= 0 && xmlParser->getAttributeValue(idx, &value) >= 0) {
984            if (value.dataType == value.TYPE_ATTRIBUTE) {
985                if (theme->getAttribute(value.data, &value, &styleBagTypeSetFlags) < 0) {
986                    value.dataType = Res_value::TYPE_NULL;
987                }
988            }
989            if (value.dataType == value.TYPE_REFERENCE) {
990                style = value.data;
991            }
992        }
993    }
994
995    // Now lock down the resource object and start pulling stuff from it.
996    res.lock();
997
998    // Retrieve the default style bag, if requested.
999    const ResTable::bag_entry* defStyleEnt = NULL;
1000    uint32_t defStyleTypeSetFlags = 0;
1001    ssize_t bagOff = defStyleRes != 0
1002            ? res.getBagLocked(defStyleRes, &defStyleEnt, &defStyleTypeSetFlags) : -1;
1003    defStyleTypeSetFlags |= defStyleBagTypeSetFlags;
1004    const ResTable::bag_entry* endDefStyleEnt = defStyleEnt +
1005        (bagOff >= 0 ? bagOff : 0);
1006
1007    // Retrieve the style class bag, if requested.
1008    const ResTable::bag_entry* styleEnt = NULL;
1009    uint32_t styleTypeSetFlags = 0;
1010    bagOff = style != 0 ? res.getBagLocked(style, &styleEnt, &styleTypeSetFlags) : -1;
1011    styleTypeSetFlags |= styleBagTypeSetFlags;
1012    const ResTable::bag_entry* endStyleEnt = styleEnt +
1013        (bagOff >= 0 ? bagOff : 0);
1014
1015    // Retrieve the XML attributes, if requested.
1016    const jsize NX = xmlParser ? xmlParser->getAttributeCount() : 0;
1017    jsize ix=0;
1018    uint32_t curXmlAttr = xmlParser ? xmlParser->getAttributeNameResID(ix) : 0;
1019
1020    static const ssize_t kXmlBlock = 0x10000000;
1021
1022    // Now iterate through all of the attributes that the client has requested,
1023    // filling in each with whatever data we can find.
1024    ssize_t block = 0;
1025    uint32_t typeSetFlags;
1026    for (jsize ii=0; ii<NI; ii++) {
1027        const uint32_t curIdent = (uint32_t)src[ii];
1028
1029        DEBUG_STYLES(LOGI("RETRIEVING ATTR 0x%08x...", curIdent));
1030
1031        // Try to find a value for this attribute...  we prioritize values
1032        // coming from, first XML attributes, then XML style, then default
1033        // style, and finally the theme.
1034        value.dataType = Res_value::TYPE_NULL;
1035        value.data = 0;
1036        typeSetFlags = 0;
1037        config.density = 0;
1038
1039        // Skip through XML attributes until the end or the next possible match.
1040        while (ix < NX && curIdent > curXmlAttr) {
1041            ix++;
1042            curXmlAttr = xmlParser->getAttributeNameResID(ix);
1043        }
1044        // Retrieve the current XML attribute if it matches, and step to next.
1045        if (ix < NX && curIdent == curXmlAttr) {
1046            block = kXmlBlock;
1047            xmlParser->getAttributeValue(ix, &value);
1048            ix++;
1049            curXmlAttr = xmlParser->getAttributeNameResID(ix);
1050            DEBUG_STYLES(LOGI("-> From XML: type=0x%x, data=0x%08x",
1051                    value.dataType, value.data));
1052        }
1053
1054        // Skip through the style values until the end or the next possible match.
1055        while (styleEnt < endStyleEnt && curIdent > styleEnt->map.name.ident) {
1056            styleEnt++;
1057        }
1058        // Retrieve the current style attribute if it matches, and step to next.
1059        if (styleEnt < endStyleEnt && curIdent == styleEnt->map.name.ident) {
1060            if (value.dataType == Res_value::TYPE_NULL) {
1061                block = styleEnt->stringBlock;
1062                typeSetFlags = styleTypeSetFlags;
1063                value = styleEnt->map.value;
1064                DEBUG_STYLES(LOGI("-> From style: type=0x%x, data=0x%08x",
1065                        value.dataType, value.data));
1066            }
1067            styleEnt++;
1068        }
1069
1070        // Skip through the default style values until the end or the next possible match.
1071        while (defStyleEnt < endDefStyleEnt && curIdent > defStyleEnt->map.name.ident) {
1072            defStyleEnt++;
1073        }
1074        // Retrieve the current default style attribute if it matches, and step to next.
1075        if (defStyleEnt < endDefStyleEnt && curIdent == defStyleEnt->map.name.ident) {
1076            if (value.dataType == Res_value::TYPE_NULL) {
1077                block = defStyleEnt->stringBlock;
1078                typeSetFlags = defStyleTypeSetFlags;
1079                value = defStyleEnt->map.value;
1080                DEBUG_STYLES(LOGI("-> From def style: type=0x%x, data=0x%08x",
1081                        value.dataType, value.data));
1082            }
1083            defStyleEnt++;
1084        }
1085
1086        uint32_t resid = 0;
1087        if (value.dataType != Res_value::TYPE_NULL) {
1088            // Take care of resolving the found resource to its final value.
1089            ssize_t newBlock = theme->resolveAttributeReference(&value, block,
1090                    &resid, &typeSetFlags, &config);
1091            if (newBlock >= 0) block = newBlock;
1092            DEBUG_STYLES(LOGI("-> Resolved attr: type=0x%x, data=0x%08x",
1093                    value.dataType, value.data));
1094        } else {
1095            // If we still don't have a value for this attribute, try to find
1096            // it in the theme!
1097            ssize_t newBlock = theme->getAttribute(curIdent, &value, &typeSetFlags);
1098            if (newBlock >= 0) {
1099                DEBUG_STYLES(LOGI("-> From theme: type=0x%x, data=0x%08x",
1100                        value.dataType, value.data));
1101                newBlock = res.resolveReference(&value, block, &resid,
1102                        &typeSetFlags, &config);
1103#if THROW_ON_BAD_ID
1104                if (newBlock == BAD_INDEX) {
1105                    jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1106                    return JNI_FALSE;
1107                }
1108#endif
1109                if (newBlock >= 0) block = newBlock;
1110                DEBUG_STYLES(LOGI("-> Resolved theme: type=0x%x, data=0x%08x",
1111                        value.dataType, value.data));
1112            }
1113        }
1114
1115        // Deal with the special @null value -- it turns back to TYPE_NULL.
1116        if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
1117            DEBUG_STYLES(LOGI("-> Setting to @null!"));
1118            value.dataType = Res_value::TYPE_NULL;
1119            block = kXmlBlock;
1120        }
1121
1122        DEBUG_STYLES(LOGI("Attribute 0x%08x: type=0x%x, data=0x%08x",
1123                curIdent, value.dataType, value.data));
1124
1125        // Write the final value back to Java.
1126        dest[STYLE_TYPE] = value.dataType;
1127        dest[STYLE_DATA] = value.data;
1128        dest[STYLE_ASSET_COOKIE] =
1129            block != kXmlBlock ? (jint)res.getTableCookie(block) : (jint)-1;
1130        dest[STYLE_RESOURCE_ID] = resid;
1131        dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
1132        dest[STYLE_DENSITY] = config.density;
1133
1134        if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
1135            indicesIdx++;
1136            indices[indicesIdx] = ii;
1137        }
1138
1139        dest += STYLE_NUM_ENTRIES;
1140    }
1141
1142    res.unlock();
1143
1144    if (indices != NULL) {
1145        indices[0] = indicesIdx;
1146        env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
1147    }
1148    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1149    env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1150
1151    return JNI_TRUE;
1152}
1153
1154static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz,
1155                                                        jint xmlParserToken,
1156                                                        jintArray attrs,
1157                                                        jintArray outValues,
1158                                                        jintArray outIndices)
1159{
1160    if (xmlParserToken == 0) {
1161        jniThrowException(env, "java/lang/NullPointerException", "xmlParserToken");
1162        return JNI_FALSE;
1163    }
1164    if (attrs == NULL) {
1165        jniThrowException(env, "java/lang/NullPointerException", "attrs");
1166        return JNI_FALSE;
1167    }
1168    if (outValues == NULL) {
1169        jniThrowException(env, "java/lang/NullPointerException", "out values");
1170        return JNI_FALSE;
1171    }
1172
1173    AssetManager* am = assetManagerForJavaObject(env, clazz);
1174    if (am == NULL) {
1175        return JNI_FALSE;
1176    }
1177    const ResTable& res(am->getResources());
1178    ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
1179    ResTable_config config;
1180    Res_value value;
1181
1182    const jsize NI = env->GetArrayLength(attrs);
1183    const jsize NV = env->GetArrayLength(outValues);
1184    if (NV < (NI*STYLE_NUM_ENTRIES)) {
1185        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
1186        return JNI_FALSE;
1187    }
1188
1189    jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
1190    if (src == NULL) {
1191        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1192        return JNI_FALSE;
1193    }
1194
1195    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1196    jint* dest = baseDest;
1197    if (dest == NULL) {
1198        env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1199        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1200        return JNI_FALSE;
1201    }
1202
1203    jint* indices = NULL;
1204    int indicesIdx = 0;
1205    if (outIndices != NULL) {
1206        if (env->GetArrayLength(outIndices) > NI) {
1207            indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
1208        }
1209    }
1210
1211    // Now lock down the resource object and start pulling stuff from it.
1212    res.lock();
1213
1214    // Retrieve the XML attributes, if requested.
1215    const jsize NX = xmlParser->getAttributeCount();
1216    jsize ix=0;
1217    uint32_t curXmlAttr = xmlParser->getAttributeNameResID(ix);
1218
1219    static const ssize_t kXmlBlock = 0x10000000;
1220
1221    // Now iterate through all of the attributes that the client has requested,
1222    // filling in each with whatever data we can find.
1223    ssize_t block = 0;
1224    uint32_t typeSetFlags;
1225    for (jsize ii=0; ii<NI; ii++) {
1226        const uint32_t curIdent = (uint32_t)src[ii];
1227
1228        // Try to find a value for this attribute...
1229        value.dataType = Res_value::TYPE_NULL;
1230        value.data = 0;
1231        typeSetFlags = 0;
1232        config.density = 0;
1233
1234        // Skip through XML attributes until the end or the next possible match.
1235        while (ix < NX && curIdent > curXmlAttr) {
1236            ix++;
1237            curXmlAttr = xmlParser->getAttributeNameResID(ix);
1238        }
1239        // Retrieve the current XML attribute if it matches, and step to next.
1240        if (ix < NX && curIdent == curXmlAttr) {
1241            block = kXmlBlock;
1242            xmlParser->getAttributeValue(ix, &value);
1243            ix++;
1244            curXmlAttr = xmlParser->getAttributeNameResID(ix);
1245        }
1246
1247        //printf("Attribute 0x%08x: type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
1248        uint32_t resid = 0;
1249        if (value.dataType != Res_value::TYPE_NULL) {
1250            // Take care of resolving the found resource to its final value.
1251            //printf("Resolving attribute reference\n");
1252            ssize_t newBlock = res.resolveReference(&value, block, &resid,
1253                    &typeSetFlags, &config);
1254#if THROW_ON_BAD_ID
1255            if (newBlock == BAD_INDEX) {
1256                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1257                return JNI_FALSE;
1258            }
1259#endif
1260            if (newBlock >= 0) block = newBlock;
1261        }
1262
1263        // Deal with the special @null value -- it turns back to TYPE_NULL.
1264        if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
1265            value.dataType = Res_value::TYPE_NULL;
1266        }
1267
1268        //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
1269
1270        // Write the final value back to Java.
1271        dest[STYLE_TYPE] = value.dataType;
1272        dest[STYLE_DATA] = value.data;
1273        dest[STYLE_ASSET_COOKIE] =
1274            block != kXmlBlock ? (jint)res.getTableCookie(block) : (jint)-1;
1275        dest[STYLE_RESOURCE_ID] = resid;
1276        dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
1277        dest[STYLE_DENSITY] = config.density;
1278
1279        if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
1280            indicesIdx++;
1281            indices[indicesIdx] = ii;
1282        }
1283
1284        dest += STYLE_NUM_ENTRIES;
1285    }
1286
1287    res.unlock();
1288
1289    if (indices != NULL) {
1290        indices[0] = indicesIdx;
1291        env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
1292    }
1293
1294    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1295    env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1296
1297    return JNI_TRUE;
1298}
1299
1300static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz,
1301                                                       jint id)
1302{
1303    AssetManager* am = assetManagerForJavaObject(env, clazz);
1304    if (am == NULL) {
1305        return 0;
1306    }
1307    const ResTable& res(am->getResources());
1308
1309    res.lock();
1310    const ResTable::bag_entry* defStyleEnt = NULL;
1311    ssize_t bagOff = res.getBagLocked(id, &defStyleEnt);
1312    res.unlock();
1313
1314    return bagOff;
1315}
1316
1317static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz,
1318                                                        jint id,
1319                                                        jintArray outValues)
1320{
1321    if (outValues == NULL) {
1322        jniThrowException(env, "java/lang/NullPointerException", "out values");
1323        return JNI_FALSE;
1324    }
1325
1326    AssetManager* am = assetManagerForJavaObject(env, clazz);
1327    if (am == NULL) {
1328        return JNI_FALSE;
1329    }
1330    const ResTable& res(am->getResources());
1331    ResTable_config config;
1332    Res_value value;
1333    ssize_t block;
1334
1335    const jsize NV = env->GetArrayLength(outValues);
1336
1337    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1338    jint* dest = baseDest;
1339    if (dest == NULL) {
1340        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1341        return JNI_FALSE;
1342    }
1343
1344    // Now lock down the resource object and start pulling stuff from it.
1345    res.lock();
1346
1347    const ResTable::bag_entry* arrayEnt = NULL;
1348    uint32_t arrayTypeSetFlags = 0;
1349    ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags);
1350    const ResTable::bag_entry* endArrayEnt = arrayEnt +
1351        (bagOff >= 0 ? bagOff : 0);
1352
1353    int i = 0;
1354    uint32_t typeSetFlags;
1355    while (i < NV && arrayEnt < endArrayEnt) {
1356        block = arrayEnt->stringBlock;
1357        typeSetFlags = arrayTypeSetFlags;
1358        config.density = 0;
1359        value = arrayEnt->map.value;
1360
1361        uint32_t resid = 0;
1362        if (value.dataType != Res_value::TYPE_NULL) {
1363            // Take care of resolving the found resource to its final value.
1364            //printf("Resolving attribute reference\n");
1365            ssize_t newBlock = res.resolveReference(&value, block, &resid,
1366                    &typeSetFlags, &config);
1367#if THROW_ON_BAD_ID
1368            if (newBlock == BAD_INDEX) {
1369                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1370                return JNI_FALSE;
1371            }
1372#endif
1373            if (newBlock >= 0) block = newBlock;
1374        }
1375
1376        // Deal with the special @null value -- it turns back to TYPE_NULL.
1377        if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
1378            value.dataType = Res_value::TYPE_NULL;
1379        }
1380
1381        //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
1382
1383        // Write the final value back to Java.
1384        dest[STYLE_TYPE] = value.dataType;
1385        dest[STYLE_DATA] = value.data;
1386        dest[STYLE_ASSET_COOKIE] = (jint)res.getTableCookie(block);
1387        dest[STYLE_RESOURCE_ID] = resid;
1388        dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
1389        dest[STYLE_DENSITY] = config.density;
1390        dest += STYLE_NUM_ENTRIES;
1391        i+= STYLE_NUM_ENTRIES;
1392        arrayEnt++;
1393    }
1394
1395    i /= STYLE_NUM_ENTRIES;
1396
1397    res.unlock();
1398
1399    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1400
1401    return i;
1402}
1403
1404static jint android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz,
1405                                                         jint cookie,
1406                                                         jstring fileName)
1407{
1408    AssetManager* am = assetManagerForJavaObject(env, clazz);
1409    if (am == NULL) {
1410        return 0;
1411    }
1412
1413    LOGV("openXmlAsset in %p (Java object %p)\n", am, clazz);
1414
1415    if (fileName == NULL) {
1416        jniThrowException(env, "java/lang/NullPointerException", "fileName");
1417        return 0;
1418    }
1419
1420    const char* fileName8 = env->GetStringUTFChars(fileName, NULL);
1421    Asset* a = cookie
1422        ? am->openNonAsset((void*)cookie, fileName8, Asset::ACCESS_BUFFER)
1423        : am->openNonAsset(fileName8, Asset::ACCESS_BUFFER);
1424
1425    if (a == NULL) {
1426        jniThrowException(env, "java/io/FileNotFoundException", fileName8);
1427        env->ReleaseStringUTFChars(fileName, fileName8);
1428        return 0;
1429    }
1430    env->ReleaseStringUTFChars(fileName, fileName8);
1431
1432    ResXMLTree* block = new ResXMLTree();
1433    status_t err = block->setTo(a->getBuffer(true), a->getLength(), true);
1434    a->close();
1435    delete a;
1436
1437    if (err != NO_ERROR) {
1438        jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
1439        return 0;
1440    }
1441
1442    return (jint)block;
1443}
1444
1445static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz,
1446                                                                 jint arrayResId)
1447{
1448    AssetManager* am = assetManagerForJavaObject(env, clazz);
1449    if (am == NULL) {
1450        return NULL;
1451    }
1452    const ResTable& res(am->getResources());
1453
1454    const ResTable::bag_entry* startOfBag;
1455    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1456    if (N < 0) {
1457        return NULL;
1458    }
1459
1460    jintArray array = env->NewIntArray(N * 2);
1461    if (array == NULL) {
1462        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1463        res.unlockBag(startOfBag);
1464        return NULL;
1465    }
1466
1467    Res_value value;
1468    const ResTable::bag_entry* bag = startOfBag;
1469    for (size_t i = 0, j = 0; ((ssize_t)i)<N; i++, bag++) {
1470        jint stringIndex = -1;
1471        jint stringBlock = 0;
1472        value = bag->map.value;
1473
1474        // Take care of resolving the found resource to its final value.
1475        stringBlock = res.resolveReference(&value, bag->stringBlock, NULL);
1476        if (value.dataType == Res_value::TYPE_STRING) {
1477            stringIndex = value.data;
1478        }
1479
1480#if THROW_ON_BAD_ID
1481        if (stringBlock == BAD_INDEX) {
1482            jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1483            return array;
1484        }
1485#endif
1486
1487        //todo: It might be faster to allocate a C array to contain
1488        //      the blocknums and indices, put them in there and then
1489        //      do just one SetIntArrayRegion()
1490        env->SetIntArrayRegion(array, j, 1, &stringBlock);
1491        env->SetIntArrayRegion(array, j + 1, 1, &stringIndex);
1492        j = j + 2;
1493    }
1494    res.unlockBag(startOfBag);
1495    return array;
1496}
1497
1498static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz,
1499                                                                        jint arrayResId)
1500{
1501    AssetManager* am = assetManagerForJavaObject(env, clazz);
1502    if (am == NULL) {
1503        return NULL;
1504    }
1505    const ResTable& res(am->getResources());
1506
1507    jclass cls = env->FindClass("java/lang/String");
1508    LOG_FATAL_IF(cls == NULL, "No string class?!?");
1509    if (cls == NULL) {
1510        return NULL;
1511    }
1512
1513    const ResTable::bag_entry* startOfBag;
1514    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1515    if (N < 0) {
1516        return NULL;
1517    }
1518
1519    jobjectArray array = env->NewObjectArray(N, cls, NULL);
1520    if (env->ExceptionCheck()) {
1521        res.unlockBag(startOfBag);
1522        return NULL;
1523    }
1524
1525    Res_value value;
1526    const ResTable::bag_entry* bag = startOfBag;
1527    size_t strLen = 0;
1528    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
1529        value = bag->map.value;
1530        jstring str = NULL;
1531
1532        // Take care of resolving the found resource to its final value.
1533        ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
1534#if THROW_ON_BAD_ID
1535        if (block == BAD_INDEX) {
1536            jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1537            return array;
1538        }
1539#endif
1540        if (value.dataType == Res_value::TYPE_STRING) {
1541            const ResStringPool* pool = res.getTableStringBlock(block);
1542            const char* str8 = pool->string8At(value.data, &strLen);
1543            if (str8 != NULL) {
1544                str = env->NewStringUTF(str8);
1545            } else {
1546                const char16_t* str16 = pool->stringAt(value.data, &strLen);
1547                str = env->NewString(str16, strLen);
1548            }
1549
1550            // If one of our NewString{UTF} calls failed due to memory, an
1551            // exception will be pending.
1552            if (env->ExceptionCheck()) {
1553                res.unlockBag(startOfBag);
1554                return NULL;
1555            }
1556
1557            env->SetObjectArrayElement(array, i, str);
1558
1559            // str is not NULL at that point, otherwise ExceptionCheck would have been true.
1560            // If we have a large amount of strings in our array, we might
1561            // overflow the local reference table of the VM.
1562            env->DeleteLocalRef(str);
1563        }
1564    }
1565    res.unlockBag(startOfBag);
1566    return array;
1567}
1568
1569static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz,
1570                                                                        jint arrayResId)
1571{
1572    AssetManager* am = assetManagerForJavaObject(env, clazz);
1573    if (am == NULL) {
1574        return NULL;
1575    }
1576    const ResTable& res(am->getResources());
1577
1578    const ResTable::bag_entry* startOfBag;
1579    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1580    if (N < 0) {
1581        return NULL;
1582    }
1583
1584    jintArray array = env->NewIntArray(N);
1585    if (array == NULL) {
1586        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1587        res.unlockBag(startOfBag);
1588        return NULL;
1589    }
1590
1591    Res_value value;
1592    const ResTable::bag_entry* bag = startOfBag;
1593    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
1594        value = bag->map.value;
1595
1596        // Take care of resolving the found resource to its final value.
1597        ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
1598#if THROW_ON_BAD_ID
1599        if (block == BAD_INDEX) {
1600            jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1601            return array;
1602        }
1603#endif
1604        if (value.dataType >= Res_value::TYPE_FIRST_INT
1605                && value.dataType <= Res_value::TYPE_LAST_INT) {
1606            int intVal = value.data;
1607            env->SetIntArrayRegion(array, i, 1, &intVal);
1608        }
1609    }
1610    res.unlockBag(startOfBag);
1611    return array;
1612}
1613
1614static void android_content_AssetManager_init(JNIEnv* env, jobject clazz)
1615{
1616    AssetManager* am = new AssetManager();
1617    if (am == NULL) {
1618        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1619        return;
1620    }
1621
1622    am->addDefaultAssets();
1623
1624    LOGV("Created AssetManager %p for Java object %p\n", am, clazz);
1625    env->SetIntField(clazz, gAssetManagerOffsets.mObject, (jint)am);
1626}
1627
1628static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz)
1629{
1630    AssetManager* am = (AssetManager*)
1631        (env->GetIntField(clazz, gAssetManagerOffsets.mObject));
1632    LOGV("Destroying AssetManager %p for Java object %p\n", am, clazz);
1633    if (am != NULL) {
1634        delete am;
1635        env->SetIntField(clazz, gAssetManagerOffsets.mObject, 0);
1636    }
1637}
1638
1639static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz)
1640{
1641    return Asset::getGlobalCount();
1642}
1643
1644static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz)
1645{
1646    String8 alloc = Asset::getAssetAllocations();
1647    if (alloc.length() <= 0) {
1648        return NULL;
1649    }
1650
1651    jstring str = env->NewStringUTF(alloc.string());
1652    return str;
1653}
1654
1655static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz)
1656{
1657    return AssetManager::getGlobalCount();
1658}
1659
1660// ----------------------------------------------------------------------------
1661
1662/*
1663 * JNI registration.
1664 */
1665static JNINativeMethod gAssetManagerMethods[] = {
1666    /* name, signature, funcPtr */
1667
1668    // Basic asset stuff.
1669    { "openAsset",      "(Ljava/lang/String;I)I",
1670        (void*) android_content_AssetManager_openAsset },
1671    { "openAssetFd",      "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1672        (void*) android_content_AssetManager_openAssetFd },
1673    { "openNonAssetNative", "(ILjava/lang/String;I)I",
1674        (void*) android_content_AssetManager_openNonAssetNative },
1675    { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1676        (void*) android_content_AssetManager_openNonAssetFdNative },
1677    { "list",           "(Ljava/lang/String;)[Ljava/lang/String;",
1678        (void*) android_content_AssetManager_list },
1679    { "destroyAsset",   "(I)V",
1680        (void*) android_content_AssetManager_destroyAsset },
1681    { "readAssetChar",  "(I)I",
1682        (void*) android_content_AssetManager_readAssetChar },
1683    { "readAsset",      "(I[BII)I",
1684        (void*) android_content_AssetManager_readAsset },
1685    { "seekAsset",      "(IJI)J",
1686        (void*) android_content_AssetManager_seekAsset },
1687    { "getAssetLength", "(I)J",
1688        (void*) android_content_AssetManager_getAssetLength },
1689    { "getAssetRemainingLength", "(I)J",
1690        (void*) android_content_AssetManager_getAssetRemainingLength },
1691    { "addAssetPath",   "(Ljava/lang/String;)I",
1692        (void*) android_content_AssetManager_addAssetPath },
1693    { "isUpToDate",     "()Z",
1694        (void*) android_content_AssetManager_isUpToDate },
1695
1696    // Resources.
1697    { "setLocale",      "(Ljava/lang/String;)V",
1698        (void*) android_content_AssetManager_setLocale },
1699    { "getLocales",      "()[Ljava/lang/String;",
1700        (void*) android_content_AssetManager_getLocales },
1701    { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIII)V",
1702        (void*) android_content_AssetManager_setConfiguration },
1703    { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
1704        (void*) android_content_AssetManager_getResourceIdentifier },
1705    { "getResourceName","(I)Ljava/lang/String;",
1706        (void*) android_content_AssetManager_getResourceName },
1707    { "getResourcePackageName","(I)Ljava/lang/String;",
1708        (void*) android_content_AssetManager_getResourcePackageName },
1709    { "getResourceTypeName","(I)Ljava/lang/String;",
1710        (void*) android_content_AssetManager_getResourceTypeName },
1711    { "getResourceEntryName","(I)Ljava/lang/String;",
1712        (void*) android_content_AssetManager_getResourceEntryName },
1713    { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I",
1714        (void*) android_content_AssetManager_loadResourceValue },
1715    { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I",
1716        (void*) android_content_AssetManager_loadResourceBagValue },
1717    { "getStringBlockCount","()I",
1718        (void*) android_content_AssetManager_getStringBlockCount },
1719    { "getNativeStringBlock","(I)I",
1720        (void*) android_content_AssetManager_getNativeStringBlock },
1721    { "getCookieName","(I)Ljava/lang/String;",
1722        (void*) android_content_AssetManager_getCookieName },
1723
1724    // Themes.
1725    { "newTheme", "()I",
1726        (void*) android_content_AssetManager_newTheme },
1727    { "deleteTheme", "(I)V",
1728        (void*) android_content_AssetManager_deleteTheme },
1729    { "applyThemeStyle", "(IIZ)V",
1730        (void*) android_content_AssetManager_applyThemeStyle },
1731    { "copyTheme", "(II)V",
1732        (void*) android_content_AssetManager_copyTheme },
1733    { "loadThemeAttributeValue", "(IILandroid/util/TypedValue;Z)I",
1734        (void*) android_content_AssetManager_loadThemeAttributeValue },
1735    { "dumpTheme", "(IILjava/lang/String;Ljava/lang/String;)V",
1736        (void*) android_content_AssetManager_dumpTheme },
1737    { "applyStyle","(IIII[I[I[I)Z",
1738        (void*) android_content_AssetManager_applyStyle },
1739    { "retrieveAttributes","(I[I[I[I)Z",
1740        (void*) android_content_AssetManager_retrieveAttributes },
1741    { "getArraySize","(I)I",
1742        (void*) android_content_AssetManager_getArraySize },
1743    { "retrieveArray","(I[I)I",
1744        (void*) android_content_AssetManager_retrieveArray },
1745
1746    // XML files.
1747    { "openXmlAssetNative", "(ILjava/lang/String;)I",
1748        (void*) android_content_AssetManager_openXmlAssetNative },
1749
1750    // Arrays.
1751    { "getArrayStringResource","(I)[Ljava/lang/String;",
1752        (void*) android_content_AssetManager_getArrayStringResource },
1753    { "getArrayStringInfo","(I)[I",
1754        (void*) android_content_AssetManager_getArrayStringInfo },
1755    { "getArrayIntResource","(I)[I",
1756        (void*) android_content_AssetManager_getArrayIntResource },
1757
1758    // Bookkeeping.
1759    { "init",           "()V",
1760        (void*) android_content_AssetManager_init },
1761    { "destroy",        "()V",
1762        (void*) android_content_AssetManager_destroy },
1763    { "getGlobalAssetCount", "()I",
1764        (void*) android_content_AssetManager_getGlobalAssetCount },
1765    { "getAssetAllocations", "()Ljava/lang/String;",
1766        (void*) android_content_AssetManager_getAssetAllocations },
1767    { "getGlobalAssetManagerCount", "()I",
1768        (void*) android_content_AssetManager_getGlobalAssetCount },
1769};
1770
1771int register_android_content_AssetManager(JNIEnv* env)
1772{
1773    jclass typedValue = env->FindClass("android/util/TypedValue");
1774    LOG_FATAL_IF(typedValue == NULL, "Unable to find class android/util/TypedValue");
1775    gTypedValueOffsets.mType
1776        = env->GetFieldID(typedValue, "type", "I");
1777    LOG_FATAL_IF(gTypedValueOffsets.mType == NULL, "Unable to find TypedValue.type");
1778    gTypedValueOffsets.mData
1779        = env->GetFieldID(typedValue, "data", "I");
1780    LOG_FATAL_IF(gTypedValueOffsets.mData == NULL, "Unable to find TypedValue.data");
1781    gTypedValueOffsets.mString
1782        = env->GetFieldID(typedValue, "string", "Ljava/lang/CharSequence;");
1783    LOG_FATAL_IF(gTypedValueOffsets.mString == NULL, "Unable to find TypedValue.string");
1784    gTypedValueOffsets.mAssetCookie
1785        = env->GetFieldID(typedValue, "assetCookie", "I");
1786    LOG_FATAL_IF(gTypedValueOffsets.mAssetCookie == NULL, "Unable to find TypedValue.assetCookie");
1787    gTypedValueOffsets.mResourceId
1788        = env->GetFieldID(typedValue, "resourceId", "I");
1789    LOG_FATAL_IF(gTypedValueOffsets.mResourceId == NULL, "Unable to find TypedValue.resourceId");
1790    gTypedValueOffsets.mChangingConfigurations
1791        = env->GetFieldID(typedValue, "changingConfigurations", "I");
1792    LOG_FATAL_IF(gTypedValueOffsets.mChangingConfigurations == NULL, "Unable to find TypedValue.changingConfigurations");
1793    gTypedValueOffsets.mDensity = env->GetFieldID(typedValue, "density", "I");
1794    LOG_FATAL_IF(gTypedValueOffsets.mDensity == NULL, "Unable to find TypedValue.density");
1795
1796    jclass assetFd = env->FindClass("android/content/res/AssetFileDescriptor");
1797    LOG_FATAL_IF(assetFd == NULL, "Unable to find class android/content/res/AssetFileDescriptor");
1798    gAssetFileDescriptorOffsets.mFd
1799        = env->GetFieldID(assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
1800    LOG_FATAL_IF(gAssetFileDescriptorOffsets.mFd == NULL, "Unable to find AssetFileDescriptor.mFd");
1801    gAssetFileDescriptorOffsets.mStartOffset
1802        = env->GetFieldID(assetFd, "mStartOffset", "J");
1803    LOG_FATAL_IF(gAssetFileDescriptorOffsets.mStartOffset == NULL, "Unable to find AssetFileDescriptor.mStartOffset");
1804    gAssetFileDescriptorOffsets.mLength
1805        = env->GetFieldID(assetFd, "mLength", "J");
1806    LOG_FATAL_IF(gAssetFileDescriptorOffsets.mLength == NULL, "Unable to find AssetFileDescriptor.mLength");
1807
1808    jclass assetManager = env->FindClass("android/content/res/AssetManager");
1809    LOG_FATAL_IF(assetManager == NULL, "Unable to find class android/content/res/AssetManager");
1810    gAssetManagerOffsets.mObject
1811        = env->GetFieldID(assetManager, "mObject", "I");
1812    LOG_FATAL_IF(gAssetManagerOffsets.mObject == NULL, "Unable to find AssetManager.mObject");
1813
1814    g_stringClass = env->FindClass("java/lang/String");
1815
1816    return AndroidRuntime::registerNativeMethods(env,
1817            "android/content/res/AssetManager", gAssetManagerMethods, NELEM(gAssetManagerMethods));
1818}
1819
1820}; // namespace android
1821