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