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