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