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