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