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