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