android_util_AssetManager.cpp revision 4d4040b7b3c46ea29a42878c14b325f09f0029ad
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    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
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 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
567static 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
607static 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
642static 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
662static 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
682static 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
702static 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
736static 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
783static 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
792static 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
802static 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
818static 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
827static 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
834static 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
843static 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
851static 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
874static 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
901static 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
1147static 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
1293static 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
1310static 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
1397static 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
1438static 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
1491static 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
1562static 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
1607static 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
1621static 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
1632static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz)
1633{
1634    return Asset::getGlobalCount();
1635}
1636
1637static 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
1648static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz)
1649{
1650    return AssetManager::getGlobalCount();
1651}
1652
1653// ----------------------------------------------------------------------------
1654
1655/*
1656 * JNI registration.
1657 */
1658static 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
1764int 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