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