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