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