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