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