android_util_AssetManager.cpp revision d7c8672ccc8ccd1f99da60accc63d6817d41ac52
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                                                           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        }
1115
1116        DEBUG_STYLES(LOGI("Attribute 0x%08x: type=0x%x, data=0x%08x",
1117                curIdent, value.dataType, value.data));
1118
1119        // Write the final value back to Java.
1120        dest[STYLE_TYPE] = value.dataType;
1121        dest[STYLE_DATA] = value.data;
1122        dest[STYLE_ASSET_COOKIE] =
1123            block != kXmlBlock ? (jint)res.getTableCookie(block) : (jint)-1;
1124        dest[STYLE_RESOURCE_ID] = resid;
1125        dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
1126        dest[STYLE_DENSITY] = config.density;
1127
1128        if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
1129            indicesIdx++;
1130            indices[indicesIdx] = ii;
1131        }
1132
1133        dest += STYLE_NUM_ENTRIES;
1134    }
1135
1136    res.unlock();
1137
1138    if (indices != NULL) {
1139        indices[0] = indicesIdx;
1140        env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
1141    }
1142    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1143    env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1144
1145    return JNI_TRUE;
1146}
1147
1148static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz,
1149                                                        jint xmlParserToken,
1150                                                        jintArray attrs,
1151                                                        jintArray outValues,
1152                                                        jintArray outIndices)
1153{
1154    if (xmlParserToken == 0) {
1155        jniThrowException(env, "java/lang/NullPointerException", "xmlParserToken");
1156        return JNI_FALSE;
1157    }
1158    if (attrs == NULL) {
1159        jniThrowException(env, "java/lang/NullPointerException", "attrs");
1160        return JNI_FALSE;
1161    }
1162    if (outValues == NULL) {
1163        jniThrowException(env, "java/lang/NullPointerException", "out values");
1164        return JNI_FALSE;
1165    }
1166
1167    AssetManager* am = assetManagerForJavaObject(env, clazz);
1168    if (am == NULL) {
1169        return JNI_FALSE;
1170    }
1171    const ResTable& res(am->getResources());
1172    ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
1173    ResTable_config config;
1174    Res_value value;
1175
1176    const jsize NI = env->GetArrayLength(attrs);
1177    const jsize NV = env->GetArrayLength(outValues);
1178    if (NV < (NI*STYLE_NUM_ENTRIES)) {
1179        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
1180        return JNI_FALSE;
1181    }
1182
1183    jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
1184    if (src == NULL) {
1185        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1186        return JNI_FALSE;
1187    }
1188
1189    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1190    jint* dest = baseDest;
1191    if (dest == NULL) {
1192        env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1193        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1194        return JNI_FALSE;
1195    }
1196
1197    jint* indices = NULL;
1198    int indicesIdx = 0;
1199    if (outIndices != NULL) {
1200        if (env->GetArrayLength(outIndices) > NI) {
1201            indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
1202        }
1203    }
1204
1205    // Now lock down the resource object and start pulling stuff from it.
1206    res.lock();
1207
1208    // Retrieve the XML attributes, if requested.
1209    const jsize NX = xmlParser->getAttributeCount();
1210    jsize ix=0;
1211    uint32_t curXmlAttr = xmlParser->getAttributeNameResID(ix);
1212
1213    static const ssize_t kXmlBlock = 0x10000000;
1214
1215    // Now iterate through all of the attributes that the client has requested,
1216    // filling in each with whatever data we can find.
1217    ssize_t block = 0;
1218    uint32_t typeSetFlags;
1219    for (jsize ii=0; ii<NI; ii++) {
1220        const uint32_t curIdent = (uint32_t)src[ii];
1221
1222        // Try to find a value for this attribute...
1223        value.dataType = Res_value::TYPE_NULL;
1224        value.data = 0;
1225        typeSetFlags = 0;
1226        config.density = 0;
1227
1228        // Skip through XML attributes until the end or the next possible match.
1229        while (ix < NX && curIdent > curXmlAttr) {
1230            ix++;
1231            curXmlAttr = xmlParser->getAttributeNameResID(ix);
1232        }
1233        // Retrieve the current XML attribute if it matches, and step to next.
1234        if (ix < NX && curIdent == curXmlAttr) {
1235            block = kXmlBlock;
1236            xmlParser->getAttributeValue(ix, &value);
1237            ix++;
1238            curXmlAttr = xmlParser->getAttributeNameResID(ix);
1239        }
1240
1241        //printf("Attribute 0x%08x: type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
1242        uint32_t resid = 0;
1243        if (value.dataType != Res_value::TYPE_NULL) {
1244            // Take care of resolving the found resource to its final value.
1245            //printf("Resolving attribute reference\n");
1246            ssize_t newBlock = res.resolveReference(&value, block, &resid,
1247                    &typeSetFlags, &config);
1248#if THROW_ON_BAD_ID
1249            if (newBlock == BAD_INDEX) {
1250                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1251                return JNI_FALSE;
1252            }
1253#endif
1254            if (newBlock >= 0) block = newBlock;
1255        }
1256
1257        // Deal with the special @null value -- it turns back to TYPE_NULL.
1258        if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
1259            value.dataType = Res_value::TYPE_NULL;
1260        }
1261
1262        //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
1263
1264        // Write the final value back to Java.
1265        dest[STYLE_TYPE] = value.dataType;
1266        dest[STYLE_DATA] = value.data;
1267        dest[STYLE_ASSET_COOKIE] =
1268            block != kXmlBlock ? (jint)res.getTableCookie(block) : (jint)-1;
1269        dest[STYLE_RESOURCE_ID] = resid;
1270        dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
1271        dest[STYLE_DENSITY] = config.density;
1272
1273        if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
1274            indicesIdx++;
1275            indices[indicesIdx] = ii;
1276        }
1277
1278        dest += STYLE_NUM_ENTRIES;
1279    }
1280
1281    res.unlock();
1282
1283    if (indices != NULL) {
1284        indices[0] = indicesIdx;
1285        env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
1286    }
1287
1288    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1289    env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1290
1291    return JNI_TRUE;
1292}
1293
1294static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz,
1295                                                       jint id)
1296{
1297    AssetManager* am = assetManagerForJavaObject(env, clazz);
1298    if (am == NULL) {
1299        return 0;
1300    }
1301    const ResTable& res(am->getResources());
1302
1303    res.lock();
1304    const ResTable::bag_entry* defStyleEnt = NULL;
1305    ssize_t bagOff = res.getBagLocked(id, &defStyleEnt);
1306    res.unlock();
1307
1308    return bagOff;
1309}
1310
1311static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz,
1312                                                        jint id,
1313                                                        jintArray outValues)
1314{
1315    if (outValues == NULL) {
1316        jniThrowException(env, "java/lang/NullPointerException", "out values");
1317        return JNI_FALSE;
1318    }
1319
1320    AssetManager* am = assetManagerForJavaObject(env, clazz);
1321    if (am == NULL) {
1322        return JNI_FALSE;
1323    }
1324    const ResTable& res(am->getResources());
1325    ResTable_config config;
1326    Res_value value;
1327    ssize_t block;
1328
1329    const jsize NV = env->GetArrayLength(outValues);
1330
1331    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1332    jint* dest = baseDest;
1333    if (dest == NULL) {
1334        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1335        return JNI_FALSE;
1336    }
1337
1338    // Now lock down the resource object and start pulling stuff from it.
1339    res.lock();
1340
1341    const ResTable::bag_entry* arrayEnt = NULL;
1342    uint32_t arrayTypeSetFlags = 0;
1343    ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags);
1344    const ResTable::bag_entry* endArrayEnt = arrayEnt +
1345        (bagOff >= 0 ? bagOff : 0);
1346
1347    int i = 0;
1348    uint32_t typeSetFlags;
1349    while (i < NV && arrayEnt < endArrayEnt) {
1350        block = arrayEnt->stringBlock;
1351        typeSetFlags = arrayTypeSetFlags;
1352        config.density = 0;
1353        value = arrayEnt->map.value;
1354
1355        uint32_t resid = 0;
1356        if (value.dataType != Res_value::TYPE_NULL) {
1357            // Take care of resolving the found resource to its final value.
1358            //printf("Resolving attribute reference\n");
1359            ssize_t newBlock = res.resolveReference(&value, block, &resid,
1360                    &typeSetFlags, &config);
1361#if THROW_ON_BAD_ID
1362            if (newBlock == BAD_INDEX) {
1363                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1364                return JNI_FALSE;
1365            }
1366#endif
1367            if (newBlock >= 0) block = newBlock;
1368        }
1369
1370        // Deal with the special @null value -- it turns back to TYPE_NULL.
1371        if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
1372            value.dataType = Res_value::TYPE_NULL;
1373        }
1374
1375        //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
1376
1377        // Write the final value back to Java.
1378        dest[STYLE_TYPE] = value.dataType;
1379        dest[STYLE_DATA] = value.data;
1380        dest[STYLE_ASSET_COOKIE] = (jint)res.getTableCookie(block);
1381        dest[STYLE_RESOURCE_ID] = resid;
1382        dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
1383        dest[STYLE_DENSITY] = config.density;
1384        dest += STYLE_NUM_ENTRIES;
1385        i+= STYLE_NUM_ENTRIES;
1386        arrayEnt++;
1387    }
1388
1389    i /= STYLE_NUM_ENTRIES;
1390
1391    res.unlock();
1392
1393    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1394
1395    return i;
1396}
1397
1398static jint android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz,
1399                                                         jint cookie,
1400                                                         jstring fileName)
1401{
1402    AssetManager* am = assetManagerForJavaObject(env, clazz);
1403    if (am == NULL) {
1404        return 0;
1405    }
1406
1407    LOGV("openXmlAsset in %p (Java object %p)\n", am, clazz);
1408
1409    if (fileName == NULL) {
1410        jniThrowException(env, "java/lang/NullPointerException", "fileName");
1411        return 0;
1412    }
1413
1414    const char* fileName8 = env->GetStringUTFChars(fileName, NULL);
1415    Asset* a = cookie
1416        ? am->openNonAsset((void*)cookie, fileName8, Asset::ACCESS_BUFFER)
1417        : am->openNonAsset(fileName8, Asset::ACCESS_BUFFER);
1418
1419    if (a == NULL) {
1420        jniThrowException(env, "java/io/FileNotFoundException", fileName8);
1421        env->ReleaseStringUTFChars(fileName, fileName8);
1422        return 0;
1423    }
1424    env->ReleaseStringUTFChars(fileName, fileName8);
1425
1426    ResXMLTree* block = new ResXMLTree();
1427    status_t err = block->setTo(a->getBuffer(true), a->getLength(), true);
1428    a->close();
1429    delete a;
1430
1431    if (err != NO_ERROR) {
1432        jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
1433        return 0;
1434    }
1435
1436    return (jint)block;
1437}
1438
1439static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz,
1440                                                                 jint arrayResId)
1441{
1442    AssetManager* am = assetManagerForJavaObject(env, clazz);
1443    if (am == NULL) {
1444        return NULL;
1445    }
1446    const ResTable& res(am->getResources());
1447
1448    const ResTable::bag_entry* startOfBag;
1449    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1450    if (N < 0) {
1451        return NULL;
1452    }
1453
1454    jintArray array = env->NewIntArray(N * 2);
1455    if (array == NULL) {
1456        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1457        res.unlockBag(startOfBag);
1458        return NULL;
1459    }
1460
1461    Res_value value;
1462    const ResTable::bag_entry* bag = startOfBag;
1463    for (size_t i = 0, j = 0; ((ssize_t)i)<N; i++, bag++) {
1464        jint stringIndex = -1;
1465        jint stringBlock = 0;
1466        value = bag->map.value;
1467
1468        // Take care of resolving the found resource to its final value.
1469        stringBlock = res.resolveReference(&value, bag->stringBlock, NULL);
1470        if (value.dataType == Res_value::TYPE_STRING) {
1471            stringIndex = value.data;
1472        }
1473
1474#if THROW_ON_BAD_ID
1475        if (stringBlock == BAD_INDEX) {
1476            jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1477            return array;
1478        }
1479#endif
1480
1481        //todo: It might be faster to allocate a C array to contain
1482        //      the blocknums and indices, put them in there and then
1483        //      do just one SetIntArrayRegion()
1484        env->SetIntArrayRegion(array, j, 1, &stringBlock);
1485        env->SetIntArrayRegion(array, j + 1, 1, &stringIndex);
1486        j = j + 2;
1487    }
1488    res.unlockBag(startOfBag);
1489    return array;
1490}
1491
1492static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz,
1493                                                                        jint arrayResId)
1494{
1495    AssetManager* am = assetManagerForJavaObject(env, clazz);
1496    if (am == NULL) {
1497        return NULL;
1498    }
1499    const ResTable& res(am->getResources());
1500
1501    jclass cls = env->FindClass("java/lang/String");
1502    LOG_FATAL_IF(cls == NULL, "No string class?!?");
1503    if (cls == NULL) {
1504        return NULL;
1505    }
1506
1507    const ResTable::bag_entry* startOfBag;
1508    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1509    if (N < 0) {
1510        return NULL;
1511    }
1512
1513    jobjectArray array = env->NewObjectArray(N, cls, NULL);
1514    if (env->ExceptionCheck()) {
1515        res.unlockBag(startOfBag);
1516        return NULL;
1517    }
1518
1519    Res_value value;
1520    const ResTable::bag_entry* bag = startOfBag;
1521    size_t strLen = 0;
1522    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
1523        value = bag->map.value;
1524        jstring str = NULL;
1525
1526        // Take care of resolving the found resource to its final value.
1527        ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
1528#if THROW_ON_BAD_ID
1529        if (block == BAD_INDEX) {
1530            jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1531            return array;
1532        }
1533#endif
1534        if (value.dataType == Res_value::TYPE_STRING) {
1535            const ResStringPool* pool = res.getTableStringBlock(block);
1536            const char* str8 = pool->string8At(value.data, &strLen);
1537            if (str8 != NULL) {
1538                str = env->NewStringUTF(str8);
1539            } else {
1540                const char16_t* str16 = pool->stringAt(value.data, &strLen);
1541                str = env->NewString(str16, strLen);
1542            }
1543
1544            // If one of our NewString{UTF} calls failed due to memory, an
1545            // exception will be pending.
1546            if (env->ExceptionCheck()) {
1547                res.unlockBag(startOfBag);
1548                return NULL;
1549            }
1550
1551            env->SetObjectArrayElement(array, i, str);
1552
1553            // str is not NULL at that point, otherwise ExceptionCheck would have been true.
1554            // If we have a large amount of strings in our array, we might
1555            // overflow the local reference table of the VM.
1556            env->DeleteLocalRef(str);
1557        }
1558    }
1559    res.unlockBag(startOfBag);
1560    return array;
1561}
1562
1563static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz,
1564                                                                        jint arrayResId)
1565{
1566    AssetManager* am = assetManagerForJavaObject(env, clazz);
1567    if (am == NULL) {
1568        return NULL;
1569    }
1570    const ResTable& res(am->getResources());
1571
1572    const ResTable::bag_entry* startOfBag;
1573    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1574    if (N < 0) {
1575        return NULL;
1576    }
1577
1578    jintArray array = env->NewIntArray(N);
1579    if (array == NULL) {
1580        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1581        res.unlockBag(startOfBag);
1582        return NULL;
1583    }
1584
1585    Res_value value;
1586    const ResTable::bag_entry* bag = startOfBag;
1587    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
1588        value = bag->map.value;
1589
1590        // Take care of resolving the found resource to its final value.
1591        ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
1592#if THROW_ON_BAD_ID
1593        if (block == BAD_INDEX) {
1594            jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1595            return array;
1596        }
1597#endif
1598        if (value.dataType >= Res_value::TYPE_FIRST_INT
1599                && value.dataType <= Res_value::TYPE_LAST_INT) {
1600            int intVal = value.data;
1601            env->SetIntArrayRegion(array, i, 1, &intVal);
1602        }
1603    }
1604    res.unlockBag(startOfBag);
1605    return array;
1606}
1607
1608static void android_content_AssetManager_init(JNIEnv* env, jobject clazz)
1609{
1610    AssetManager* am = new AssetManager();
1611    if (am == NULL) {
1612        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1613        return;
1614    }
1615
1616    am->addDefaultAssets();
1617
1618    LOGV("Created AssetManager %p for Java object %p\n", am, clazz);
1619    env->SetIntField(clazz, gAssetManagerOffsets.mObject, (jint)am);
1620}
1621
1622static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz)
1623{
1624    AssetManager* am = (AssetManager*)
1625        (env->GetIntField(clazz, gAssetManagerOffsets.mObject));
1626    LOGV("Destroying AssetManager %p for Java object %p\n", am, clazz);
1627    if (am != NULL) {
1628        delete am;
1629        env->SetIntField(clazz, gAssetManagerOffsets.mObject, 0);
1630    }
1631}
1632
1633static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz)
1634{
1635    return Asset::getGlobalCount();
1636}
1637
1638static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz)
1639{
1640    String8 alloc = Asset::getAssetAllocations();
1641    if (alloc.length() <= 0) {
1642        return NULL;
1643    }
1644
1645    jstring str = env->NewStringUTF(alloc.string());
1646    return str;
1647}
1648
1649static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz)
1650{
1651    return AssetManager::getGlobalCount();
1652}
1653
1654// ----------------------------------------------------------------------------
1655
1656/*
1657 * JNI registration.
1658 */
1659static JNINativeMethod gAssetManagerMethods[] = {
1660    /* name, signature, funcPtr */
1661
1662    // Basic asset stuff.
1663    { "openAsset",      "(Ljava/lang/String;I)I",
1664        (void*) android_content_AssetManager_openAsset },
1665    { "openAssetFd",      "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1666        (void*) android_content_AssetManager_openAssetFd },
1667    { "openNonAssetNative", "(ILjava/lang/String;I)I",
1668        (void*) android_content_AssetManager_openNonAssetNative },
1669    { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1670        (void*) android_content_AssetManager_openNonAssetFdNative },
1671    { "list",           "(Ljava/lang/String;)[Ljava/lang/String;",
1672        (void*) android_content_AssetManager_list },
1673    { "destroyAsset",   "(I)V",
1674        (void*) android_content_AssetManager_destroyAsset },
1675    { "readAssetChar",  "(I)I",
1676        (void*) android_content_AssetManager_readAssetChar },
1677    { "readAsset",      "(I[BII)I",
1678        (void*) android_content_AssetManager_readAsset },
1679    { "seekAsset",      "(IJI)J",
1680        (void*) android_content_AssetManager_seekAsset },
1681    { "getAssetLength", "(I)J",
1682        (void*) android_content_AssetManager_getAssetLength },
1683    { "getAssetRemainingLength", "(I)J",
1684        (void*) android_content_AssetManager_getAssetRemainingLength },
1685    { "addAssetPath",   "(Ljava/lang/String;)I",
1686        (void*) android_content_AssetManager_addAssetPath },
1687    { "isUpToDate",     "()Z",
1688        (void*) android_content_AssetManager_isUpToDate },
1689
1690    // Resources.
1691    { "setLocale",      "(Ljava/lang/String;)V",
1692        (void*) android_content_AssetManager_setLocale },
1693    { "getLocales",      "()[Ljava/lang/String;",
1694        (void*) android_content_AssetManager_getLocales },
1695    { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIII)V",
1696        (void*) android_content_AssetManager_setConfiguration },
1697    { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
1698        (void*) android_content_AssetManager_getResourceIdentifier },
1699    { "getResourceName","(I)Ljava/lang/String;",
1700        (void*) android_content_AssetManager_getResourceName },
1701    { "getResourcePackageName","(I)Ljava/lang/String;",
1702        (void*) android_content_AssetManager_getResourcePackageName },
1703    { "getResourceTypeName","(I)Ljava/lang/String;",
1704        (void*) android_content_AssetManager_getResourceTypeName },
1705    { "getResourceEntryName","(I)Ljava/lang/String;",
1706        (void*) android_content_AssetManager_getResourceEntryName },
1707    { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I",
1708        (void*) android_content_AssetManager_loadResourceValue },
1709    { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I",
1710        (void*) android_content_AssetManager_loadResourceBagValue },
1711    { "getStringBlockCount","()I",
1712        (void*) android_content_AssetManager_getStringBlockCount },
1713    { "getNativeStringBlock","(I)I",
1714        (void*) android_content_AssetManager_getNativeStringBlock },
1715    { "getCookieName","(I)Ljava/lang/String;",
1716        (void*) android_content_AssetManager_getCookieName },
1717
1718    // Themes.
1719    { "newTheme", "()I",
1720        (void*) android_content_AssetManager_newTheme },
1721    { "deleteTheme", "(I)V",
1722        (void*) android_content_AssetManager_deleteTheme },
1723    { "applyThemeStyle", "(IIZ)V",
1724        (void*) android_content_AssetManager_applyThemeStyle },
1725    { "copyTheme", "(II)V",
1726        (void*) android_content_AssetManager_copyTheme },
1727    { "loadThemeAttributeValue", "(IILandroid/util/TypedValue;Z)I",
1728        (void*) android_content_AssetManager_loadThemeAttributeValue },
1729    { "dumpTheme", "(IILjava/lang/String;Ljava/lang/String;)V",
1730        (void*) android_content_AssetManager_dumpTheme },
1731    { "applyStyle","(IIII[I[I[I)Z",
1732        (void*) android_content_AssetManager_applyStyle },
1733    { "retrieveAttributes","(I[I[I[I)Z",
1734        (void*) android_content_AssetManager_retrieveAttributes },
1735    { "getArraySize","(I)I",
1736        (void*) android_content_AssetManager_getArraySize },
1737    { "retrieveArray","(I[I)I",
1738        (void*) android_content_AssetManager_retrieveArray },
1739
1740    // XML files.
1741    { "openXmlAssetNative", "(ILjava/lang/String;)I",
1742        (void*) android_content_AssetManager_openXmlAssetNative },
1743
1744    // Arrays.
1745    { "getArrayStringResource","(I)[Ljava/lang/String;",
1746        (void*) android_content_AssetManager_getArrayStringResource },
1747    { "getArrayStringInfo","(I)[I",
1748        (void*) android_content_AssetManager_getArrayStringInfo },
1749    { "getArrayIntResource","(I)[I",
1750        (void*) android_content_AssetManager_getArrayIntResource },
1751
1752    // Bookkeeping.
1753    { "init",           "()V",
1754        (void*) android_content_AssetManager_init },
1755    { "destroy",        "()V",
1756        (void*) android_content_AssetManager_destroy },
1757    { "getGlobalAssetCount", "()I",
1758        (void*) android_content_AssetManager_getGlobalAssetCount },
1759    { "getAssetAllocations", "()Ljava/lang/String;",
1760        (void*) android_content_AssetManager_getAssetAllocations },
1761    { "getGlobalAssetManagerCount", "()I",
1762        (void*) android_content_AssetManager_getGlobalAssetCount },
1763};
1764
1765int register_android_content_AssetManager(JNIEnv* env)
1766{
1767    jclass typedValue = env->FindClass("android/util/TypedValue");
1768    LOG_FATAL_IF(typedValue == NULL, "Unable to find class android/util/TypedValue");
1769    gTypedValueOffsets.mType
1770        = env->GetFieldID(typedValue, "type", "I");
1771    LOG_FATAL_IF(gTypedValueOffsets.mType == NULL, "Unable to find TypedValue.type");
1772    gTypedValueOffsets.mData
1773        = env->GetFieldID(typedValue, "data", "I");
1774    LOG_FATAL_IF(gTypedValueOffsets.mData == NULL, "Unable to find TypedValue.data");
1775    gTypedValueOffsets.mString
1776        = env->GetFieldID(typedValue, "string", "Ljava/lang/CharSequence;");
1777    LOG_FATAL_IF(gTypedValueOffsets.mString == NULL, "Unable to find TypedValue.string");
1778    gTypedValueOffsets.mAssetCookie
1779        = env->GetFieldID(typedValue, "assetCookie", "I");
1780    LOG_FATAL_IF(gTypedValueOffsets.mAssetCookie == NULL, "Unable to find TypedValue.assetCookie");
1781    gTypedValueOffsets.mResourceId
1782        = env->GetFieldID(typedValue, "resourceId", "I");
1783    LOG_FATAL_IF(gTypedValueOffsets.mResourceId == NULL, "Unable to find TypedValue.resourceId");
1784    gTypedValueOffsets.mChangingConfigurations
1785        = env->GetFieldID(typedValue, "changingConfigurations", "I");
1786    LOG_FATAL_IF(gTypedValueOffsets.mChangingConfigurations == NULL, "Unable to find TypedValue.changingConfigurations");
1787    gTypedValueOffsets.mDensity = env->GetFieldID(typedValue, "density", "I");
1788    LOG_FATAL_IF(gTypedValueOffsets.mDensity == NULL, "Unable to find TypedValue.density");
1789
1790    jclass assetFd = env->FindClass("android/content/res/AssetFileDescriptor");
1791    LOG_FATAL_IF(assetFd == NULL, "Unable to find class android/content/res/AssetFileDescriptor");
1792    gAssetFileDescriptorOffsets.mFd
1793        = env->GetFieldID(assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
1794    LOG_FATAL_IF(gAssetFileDescriptorOffsets.mFd == NULL, "Unable to find AssetFileDescriptor.mFd");
1795    gAssetFileDescriptorOffsets.mStartOffset
1796        = env->GetFieldID(assetFd, "mStartOffset", "J");
1797    LOG_FATAL_IF(gAssetFileDescriptorOffsets.mStartOffset == NULL, "Unable to find AssetFileDescriptor.mStartOffset");
1798    gAssetFileDescriptorOffsets.mLength
1799        = env->GetFieldID(assetFd, "mLength", "J");
1800    LOG_FATAL_IF(gAssetFileDescriptorOffsets.mLength == NULL, "Unable to find AssetFileDescriptor.mLength");
1801
1802    jclass assetManager = env->FindClass("android/content/res/AssetManager");
1803    LOG_FATAL_IF(assetManager == NULL, "Unable to find class android/content/res/AssetManager");
1804    gAssetManagerOffsets.mObject
1805        = env->GetFieldID(assetManager, "mObject", "I");
1806    LOG_FATAL_IF(gAssetManagerOffsets.mObject == NULL, "Unable to find AssetManager.mObject");
1807
1808    g_stringClass = env->FindClass("java/lang/String");
1809
1810    return AndroidRuntime::registerNativeMethods(env,
1811            "android/content/res/AssetManager", gAssetManagerMethods, NELEM(gAssetManagerMethods));
1812}
1813
1814}; // namespace android
1815