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 <inttypes.h>
23#include <linux/capability.h>
24#include <stdio.h>
25#include <sys/types.h>
26#include <sys/wait.h>
27#include <sys/stat.h>
28
29#include <private/android_filesystem_config.h> // for AID_SYSTEM
30
31#include "androidfw/Asset.h"
32#include "androidfw/AssetManager.h"
33#include "androidfw/AttributeFinder.h"
34#include "androidfw/ResourceTypes.h"
35#include "android_runtime/AndroidRuntime.h"
36#include "android_util_Binder.h"
37#include "core_jni_helpers.h"
38#include "jni.h"
39#include "JNIHelp.h"
40#include "ScopedStringChars.h"
41#include "ScopedUtfChars.h"
42#include "utils/Log.h"
43#include "utils/misc.h"
44
45extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
46extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
47
48
49namespace android {
50
51static const bool kThrowOnBadId = false;
52static const bool kDebugStyles = false;
53
54// ----------------------------------------------------------------------------
55
56static struct typedvalue_offsets_t
57{
58    jfieldID mType;
59    jfieldID mData;
60    jfieldID mString;
61    jfieldID mAssetCookie;
62    jfieldID mResourceId;
63    jfieldID mChangingConfigurations;
64    jfieldID mDensity;
65} gTypedValueOffsets;
66
67static struct assetfiledescriptor_offsets_t
68{
69    jfieldID mFd;
70    jfieldID mStartOffset;
71    jfieldID mLength;
72} gAssetFileDescriptorOffsets;
73
74static struct assetmanager_offsets_t
75{
76    jfieldID mObject;
77} gAssetManagerOffsets;
78
79static struct sparsearray_offsets_t
80{
81    jclass classObject;
82    jmethodID constructor;
83    jmethodID put;
84} gSparseArrayOffsets;
85
86static struct configuration_offsets_t
87{
88    jclass classObject;
89    jmethodID constructor;
90    jfieldID mSmallestScreenWidthDpOffset;
91    jfieldID mScreenWidthDpOffset;
92    jfieldID mScreenHeightDpOffset;
93} gConfigurationOffsets;
94
95jclass g_stringClass = NULL;
96
97// ----------------------------------------------------------------------------
98
99enum {
100    STYLE_NUM_ENTRIES = 6,
101    STYLE_TYPE = 0,
102    STYLE_DATA = 1,
103    STYLE_ASSET_COOKIE = 2,
104    STYLE_RESOURCE_ID = 3,
105    STYLE_CHANGING_CONFIGURATIONS = 4,
106    STYLE_DENSITY = 5
107};
108
109static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
110                      const Res_value& value, uint32_t ref, ssize_t block,
111                      uint32_t typeSpecFlags, ResTable_config* config = NULL);
112
113jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
114               const Res_value& value, uint32_t ref, ssize_t block,
115               uint32_t typeSpecFlags, ResTable_config* config)
116{
117    env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType);
118    env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie,
119                     static_cast<jint>(table->getTableCookie(block)));
120    env->SetIntField(outValue, gTypedValueOffsets.mData, value.data);
121    env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL);
122    env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref);
123    env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations,
124            typeSpecFlags);
125    if (config != NULL) {
126        env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density);
127    }
128    return block;
129}
130
131// This is called by zygote (running as user root) as part of preloadResources.
132static void verifySystemIdmaps()
133{
134    pid_t pid;
135    char system_id[10];
136
137    snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM);
138
139    switch (pid = fork()) {
140        case -1:
141            ALOGE("failed to fork for idmap: %s", strerror(errno));
142            break;
143        case 0: // child
144            {
145                struct __user_cap_header_struct capheader;
146                struct __user_cap_data_struct capdata;
147
148                memset(&capheader, 0, sizeof(capheader));
149                memset(&capdata, 0, sizeof(capdata));
150
151                capheader.version = _LINUX_CAPABILITY_VERSION;
152                capheader.pid = 0;
153
154                if (capget(&capheader, &capdata) != 0) {
155                    ALOGE("capget: %s\n", strerror(errno));
156                    exit(1);
157                }
158
159                capdata.effective = capdata.permitted;
160                if (capset(&capheader, &capdata) != 0) {
161                    ALOGE("capset: %s\n", strerror(errno));
162                    exit(1);
163                }
164
165                if (setgid(AID_SYSTEM) != 0) {
166                    ALOGE("setgid: %s\n", strerror(errno));
167                    exit(1);
168                }
169
170                if (setuid(AID_SYSTEM) != 0) {
171                    ALOGE("setuid: %s\n", strerror(errno));
172                    exit(1);
173                }
174
175                // Generic idmap parameters
176                const char* argv[7];
177                int argc = 0;
178                struct stat st;
179
180                memset(argv, NULL, sizeof(argv));
181                argv[argc++] = AssetManager::IDMAP_BIN;
182                argv[argc++] = "--scan";
183                argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
184                argv[argc++] = AssetManager::TARGET_APK_PATH;
185                argv[argc++] = AssetManager::IDMAP_DIR;
186
187                // Directories to scan for overlays
188                // /vendor/overlay
189                if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
190                    argv[argc++] = AssetManager::OVERLAY_DIR;
191                 }
192
193                // Finally, invoke idmap (if any overlay directory exists)
194                if (argc > 5) {
195                    execv(AssetManager::IDMAP_BIN, (char* const*)argv);
196                    ALOGE("failed to execl for idmap: %s", strerror(errno));
197                    exit(1); // should never get here
198                } else {
199                    exit(0);
200                }
201            }
202            break;
203        default: // parent
204            waitpid(pid, NULL, 0);
205            break;
206    }
207}
208
209// ----------------------------------------------------------------------------
210
211// this guy is exported to other jni routines
212AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj)
213{
214    jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject);
215    AssetManager* am = reinterpret_cast<AssetManager*>(amHandle);
216    if (am != NULL) {
217        return am;
218    }
219    jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
220    return NULL;
221}
222
223static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz,
224                                                jstring fileName, jint mode)
225{
226    AssetManager* am = assetManagerForJavaObject(env, clazz);
227    if (am == NULL) {
228        return 0;
229    }
230
231    ALOGV("openAsset in %p (Java object %p)\n", am, clazz);
232
233    ScopedUtfChars fileName8(env, fileName);
234    if (fileName8.c_str() == NULL) {
235        jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name");
236        return -1;
237    }
238
239    if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
240        && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
241        jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
242        return -1;
243    }
244
245    Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode);
246
247    if (a == NULL) {
248        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
249        return -1;
250    }
251
252    //printf("Created Asset Stream: %p\n", a);
253
254    return reinterpret_cast<jlong>(a);
255}
256
257static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets)
258{
259    off64_t startOffset, length;
260    int fd = a->openFileDescriptor(&startOffset, &length);
261    delete a;
262
263    if (fd < 0) {
264        jniThrowException(env, "java/io/FileNotFoundException",
265                "This file can not be opened as a file descriptor; it is probably compressed");
266        return NULL;
267    }
268
269    jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0);
270    if (offsets == NULL) {
271        close(fd);
272        return NULL;
273    }
274
275    offsets[0] = startOffset;
276    offsets[1] = length;
277
278    env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0);
279
280    jobject fileDesc = jniCreateFileDescriptor(env, fd);
281    if (fileDesc == NULL) {
282        close(fd);
283        return NULL;
284    }
285
286    return newParcelFileDescriptor(env, fileDesc);
287}
288
289static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz,
290                                                jstring fileName, jlongArray outOffsets)
291{
292    AssetManager* am = assetManagerForJavaObject(env, clazz);
293    if (am == NULL) {
294        return NULL;
295    }
296
297    ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz);
298
299    ScopedUtfChars fileName8(env, fileName);
300    if (fileName8.c_str() == NULL) {
301        return NULL;
302    }
303
304    Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM);
305
306    if (a == NULL) {
307        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
308        return NULL;
309    }
310
311    //printf("Created Asset Stream: %p\n", a);
312
313    return returnParcelFileDescriptor(env, a, outOffsets);
314}
315
316static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz,
317                                                         jint cookie,
318                                                         jstring fileName,
319                                                         jint mode)
320{
321    AssetManager* am = assetManagerForJavaObject(env, clazz);
322    if (am == NULL) {
323        return 0;
324    }
325
326    ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz);
327
328    ScopedUtfChars fileName8(env, fileName);
329    if (fileName8.c_str() == NULL) {
330        return -1;
331    }
332
333    if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
334        && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
335        jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
336        return -1;
337    }
338
339    Asset* a = cookie
340        ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(),
341                (Asset::AccessMode)mode)
342        : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode);
343
344    if (a == NULL) {
345        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
346        return -1;
347    }
348
349    //printf("Created Asset Stream: %p\n", a);
350
351    return reinterpret_cast<jlong>(a);
352}
353
354static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz,
355                                                         jint cookie,
356                                                         jstring fileName,
357                                                         jlongArray outOffsets)
358{
359    AssetManager* am = assetManagerForJavaObject(env, clazz);
360    if (am == NULL) {
361        return NULL;
362    }
363
364    ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz);
365
366    ScopedUtfChars fileName8(env, fileName);
367    if (fileName8.c_str() == NULL) {
368        return NULL;
369    }
370
371    Asset* a = cookie
372        ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM)
373        : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM);
374
375    if (a == NULL) {
376        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
377        return NULL;
378    }
379
380    //printf("Created Asset Stream: %p\n", a);
381
382    return returnParcelFileDescriptor(env, a, outOffsets);
383}
384
385static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz,
386                                                   jstring fileName)
387{
388    AssetManager* am = assetManagerForJavaObject(env, clazz);
389    if (am == NULL) {
390        return NULL;
391    }
392
393    ScopedUtfChars fileName8(env, fileName);
394    if (fileName8.c_str() == NULL) {
395        return NULL;
396    }
397
398    AssetDir* dir = am->openDir(fileName8.c_str());
399
400    if (dir == NULL) {
401        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
402        return NULL;
403    }
404
405    size_t N = dir->getFileCount();
406
407    jobjectArray array = env->NewObjectArray(dir->getFileCount(),
408                                                g_stringClass, NULL);
409    if (array == NULL) {
410        delete dir;
411        return NULL;
412    }
413
414    for (size_t i=0; i<N; i++) {
415        const String8& name = dir->getFileName(i);
416        jstring str = env->NewStringUTF(name.string());
417        if (str == NULL) {
418            delete dir;
419            return NULL;
420        }
421        env->SetObjectArrayElement(array, i, str);
422        env->DeleteLocalRef(str);
423    }
424
425    delete dir;
426
427    return array;
428}
429
430static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz,
431                                                      jlong assetHandle)
432{
433    Asset* a = reinterpret_cast<Asset*>(assetHandle);
434
435    //printf("Destroying Asset Stream: %p\n", a);
436
437    if (a == NULL) {
438        jniThrowNullPointerException(env, "asset");
439        return;
440    }
441
442    delete a;
443}
444
445static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz,
446                                                       jlong assetHandle)
447{
448    Asset* a = reinterpret_cast<Asset*>(assetHandle);
449
450    if (a == NULL) {
451        jniThrowNullPointerException(env, "asset");
452        return -1;
453    }
454
455    uint8_t b;
456    ssize_t res = a->read(&b, 1);
457    return res == 1 ? b : -1;
458}
459
460static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz,
461                                                jlong assetHandle, jbyteArray bArray,
462                                                jint off, jint len)
463{
464    Asset* a = reinterpret_cast<Asset*>(assetHandle);
465
466    if (a == NULL || bArray == NULL) {
467        jniThrowNullPointerException(env, "asset");
468        return -1;
469    }
470
471    if (len == 0) {
472        return 0;
473    }
474
475    jsize bLen = env->GetArrayLength(bArray);
476    if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) {
477        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
478        return -1;
479    }
480
481    jbyte* b = env->GetByteArrayElements(bArray, NULL);
482    ssize_t res = a->read(b+off, len);
483    env->ReleaseByteArrayElements(bArray, b, 0);
484
485    if (res > 0) return static_cast<jint>(res);
486
487    if (res < 0) {
488        jniThrowException(env, "java/io/IOException", "");
489    }
490    return -1;
491}
492
493static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz,
494                                                 jlong assetHandle,
495                                                 jlong offset, jint whence)
496{
497    Asset* a = reinterpret_cast<Asset*>(assetHandle);
498
499    if (a == NULL) {
500        jniThrowNullPointerException(env, "asset");
501        return -1;
502    }
503
504    return a->seek(
505        offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR));
506}
507
508static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz,
509                                                      jlong assetHandle)
510{
511    Asset* a = reinterpret_cast<Asset*>(assetHandle);
512
513    if (a == NULL) {
514        jniThrowNullPointerException(env, "asset");
515        return -1;
516    }
517
518    return a->getLength();
519}
520
521static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz,
522                                                               jlong assetHandle)
523{
524    Asset* a = reinterpret_cast<Asset*>(assetHandle);
525
526    if (a == NULL) {
527        jniThrowNullPointerException(env, "asset");
528        return -1;
529    }
530
531    return a->getRemainingLength();
532}
533
534static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,
535                                                       jstring path, jboolean appAsLib)
536{
537    ScopedUtfChars path8(env, path);
538    if (path8.c_str() == NULL) {
539        return 0;
540    }
541
542    AssetManager* am = assetManagerForJavaObject(env, clazz);
543    if (am == NULL) {
544        return 0;
545    }
546
547    int32_t cookie;
548    bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib);
549
550    return (res) ? static_cast<jint>(cookie) : 0;
551}
552
553static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz,
554                                                     jstring idmapPath)
555{
556    ScopedUtfChars idmapPath8(env, idmapPath);
557    if (idmapPath8.c_str() == NULL) {
558        return 0;
559    }
560
561    AssetManager* am = assetManagerForJavaObject(env, clazz);
562    if (am == NULL) {
563        return 0;
564    }
565
566    int32_t cookie;
567    bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie);
568
569    return (res) ? (jint)cookie : 0;
570}
571
572static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz)
573{
574    AssetManager* am = assetManagerForJavaObject(env, clazz);
575    if (am == NULL) {
576        return JNI_TRUE;
577    }
578    return am->isUpToDate() ? JNI_TRUE : JNI_FALSE;
579}
580
581static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales)
582{
583    Vector<String8> locales;
584
585    AssetManager* am = assetManagerForJavaObject(env, clazz);
586    if (am == NULL) {
587        return NULL;
588    }
589
590    am->getLocales(&locales, includeSystemLocales);
591
592    const int N = locales.size();
593
594    jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL);
595    if (result == NULL) {
596        return NULL;
597    }
598
599    for (int i=0; i<N; i++) {
600        jstring str = env->NewStringUTF(locales[i].string());
601        if (str == NULL) {
602            return NULL;
603        }
604        env->SetObjectArrayElement(result, i, str);
605        env->DeleteLocalRef(str);
606    }
607
608    return result;
609}
610
611static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
612{
613    return getLocales(env, clazz, true /* include system locales */);
614}
615
616static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz)
617{
618    return getLocales(env, clazz, false /* don't include system locales */);
619}
620
621static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
622    jobject result = env->NewObject(gConfigurationOffsets.classObject,
623            gConfigurationOffsets.constructor);
624    if (result == NULL) {
625        return NULL;
626    }
627
628    env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset,
629            config.smallestScreenWidthDp);
630    env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
631    env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
632
633    return result;
634}
635
636static jobjectArray getSizeConfigurationsInternal(JNIEnv* env,
637        const Vector<ResTable_config>& configs) {
638    const int N = configs.size();
639    jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL);
640    if (result == NULL) {
641        return NULL;
642    }
643
644    for (int i=0; i<N; i++) {
645        jobject config = constructConfigurationObject(env, configs[i]);
646        if (config == NULL) {
647            env->DeleteLocalRef(result);
648            return NULL;
649        }
650
651        env->SetObjectArrayElement(result, i, config);
652        env->DeleteLocalRef(config);
653    }
654
655    return result;
656}
657
658static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) {
659    AssetManager* am = assetManagerForJavaObject(env, clazz);
660    if (am == NULL) {
661        return NULL;
662    }
663
664    const ResTable& res(am->getResources());
665    Vector<ResTable_config> configs;
666    res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */);
667
668    return getSizeConfigurationsInternal(env, configs);
669}
670
671static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz,
672                                                          jint mcc, jint mnc,
673                                                          jstring locale, jint orientation,
674                                                          jint touchscreen, jint density,
675                                                          jint keyboard, jint keyboardHidden,
676                                                          jint navigation,
677                                                          jint screenWidth, jint screenHeight,
678                                                          jint smallestScreenWidthDp,
679                                                          jint screenWidthDp, jint screenHeightDp,
680                                                          jint screenLayout, jint uiMode,
681                                                          jint sdkVersion)
682{
683    AssetManager* am = assetManagerForJavaObject(env, clazz);
684    if (am == NULL) {
685        return;
686    }
687
688    ResTable_config config;
689    memset(&config, 0, sizeof(config));
690
691    const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL;
692
693    // Constants duplicated from Java class android.content.res.Configuration.
694    static const jint kScreenLayoutRoundMask = 0x300;
695    static const jint kScreenLayoutRoundShift = 8;
696
697    config.mcc = (uint16_t)mcc;
698    config.mnc = (uint16_t)mnc;
699    config.orientation = (uint8_t)orientation;
700    config.touchscreen = (uint8_t)touchscreen;
701    config.density = (uint16_t)density;
702    config.keyboard = (uint8_t)keyboard;
703    config.inputFlags = (uint8_t)keyboardHidden;
704    config.navigation = (uint8_t)navigation;
705    config.screenWidth = (uint16_t)screenWidth;
706    config.screenHeight = (uint16_t)screenHeight;
707    config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp;
708    config.screenWidthDp = (uint16_t)screenWidthDp;
709    config.screenHeightDp = (uint16_t)screenHeightDp;
710    config.screenLayout = (uint8_t)screenLayout;
711    config.uiMode = (uint8_t)uiMode;
712    config.sdkVersion = (uint16_t)sdkVersion;
713    config.minorVersion = 0;
714
715    // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
716    // in C++. We must extract the round qualifier out of the Java screenLayout and put it
717    // into screenLayout2.
718    config.screenLayout2 =
719            (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
720
721    am->setConfiguration(config, locale8);
722
723    if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8);
724}
725
726static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz,
727                                                            jstring name,
728                                                            jstring defType,
729                                                            jstring defPackage)
730{
731    ScopedStringChars name16(env, name);
732    if (name16.get() == NULL) {
733        return 0;
734    }
735
736    AssetManager* am = assetManagerForJavaObject(env, clazz);
737    if (am == NULL) {
738        return 0;
739    }
740
741    const char16_t* defType16 = reinterpret_cast<const char16_t*>(defType)
742        ? reinterpret_cast<const char16_t*>(env->GetStringChars(defType, NULL))
743        : NULL;
744    jsize defTypeLen = defType
745        ? env->GetStringLength(defType) : 0;
746    const char16_t* defPackage16 = reinterpret_cast<const char16_t*>(defPackage)
747        ? reinterpret_cast<const char16_t*>(env->GetStringChars(defPackage,
748                                                                NULL))
749        : NULL;
750    jsize defPackageLen = defPackage
751        ? env->GetStringLength(defPackage) : 0;
752
753    jint ident = am->getResources().identifierForName(
754        reinterpret_cast<const char16_t*>(name16.get()), name16.size(),
755        defType16, defTypeLen, defPackage16, defPackageLen);
756
757    if (defPackage16) {
758        env->ReleaseStringChars(defPackage,
759                                reinterpret_cast<const jchar*>(defPackage16));
760    }
761    if (defType16) {
762        env->ReleaseStringChars(defType,
763                                reinterpret_cast<const jchar*>(defType16));
764    }
765
766    return ident;
767}
768
769static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz,
770                                                            jint resid)
771{
772    AssetManager* am = assetManagerForJavaObject(env, clazz);
773    if (am == NULL) {
774        return NULL;
775    }
776
777    ResTable::resource_name name;
778    if (!am->getResources().getResourceName(resid, true, &name)) {
779        return NULL;
780    }
781
782    String16 str;
783    if (name.package != NULL) {
784        str.setTo(name.package, name.packageLen);
785    }
786    if (name.type8 != NULL || name.type != NULL) {
787        if (str.size() > 0) {
788            char16_t div = ':';
789            str.append(&div, 1);
790        }
791        if (name.type8 != NULL) {
792            str.append(String16(name.type8, name.typeLen));
793        } else {
794            str.append(name.type, name.typeLen);
795        }
796    }
797    if (name.name8 != NULL || name.name != NULL) {
798        if (str.size() > 0) {
799            char16_t div = '/';
800            str.append(&div, 1);
801        }
802        if (name.name8 != NULL) {
803            str.append(String16(name.name8, name.nameLen));
804        } else {
805            str.append(name.name, name.nameLen);
806        }
807    }
808
809    return env->NewString((const jchar*)str.string(), str.size());
810}
811
812static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz,
813                                                                   jint resid)
814{
815    AssetManager* am = assetManagerForJavaObject(env, clazz);
816    if (am == NULL) {
817        return NULL;
818    }
819
820    ResTable::resource_name name;
821    if (!am->getResources().getResourceName(resid, true, &name)) {
822        return NULL;
823    }
824
825    if (name.package != NULL) {
826        return env->NewString((const jchar*)name.package, name.packageLen);
827    }
828
829    return NULL;
830}
831
832static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz,
833                                                                jint resid)
834{
835    AssetManager* am = assetManagerForJavaObject(env, clazz);
836    if (am == NULL) {
837        return NULL;
838    }
839
840    ResTable::resource_name name;
841    if (!am->getResources().getResourceName(resid, true, &name)) {
842        return NULL;
843    }
844
845    if (name.type8 != NULL) {
846        return env->NewStringUTF(name.type8);
847    }
848
849    if (name.type != NULL) {
850        return env->NewString((const jchar*)name.type, name.typeLen);
851    }
852
853    return NULL;
854}
855
856static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz,
857                                                                 jint resid)
858{
859    AssetManager* am = assetManagerForJavaObject(env, clazz);
860    if (am == NULL) {
861        return NULL;
862    }
863
864    ResTable::resource_name name;
865    if (!am->getResources().getResourceName(resid, true, &name)) {
866        return NULL;
867    }
868
869    if (name.name8 != NULL) {
870        return env->NewStringUTF(name.name8);
871    }
872
873    if (name.name != NULL) {
874        return env->NewString((const jchar*)name.name, name.nameLen);
875    }
876
877    return NULL;
878}
879
880static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz,
881                                                           jint ident,
882                                                           jshort density,
883                                                           jobject outValue,
884                                                           jboolean resolve)
885{
886    if (outValue == NULL) {
887         jniThrowNullPointerException(env, "outValue");
888         return 0;
889    }
890    AssetManager* am = assetManagerForJavaObject(env, clazz);
891    if (am == NULL) {
892        return 0;
893    }
894    const ResTable& res(am->getResources());
895
896    Res_value value;
897    ResTable_config config;
898    uint32_t typeSpecFlags;
899    ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config);
900    if (kThrowOnBadId) {
901        if (block == BAD_INDEX) {
902            jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
903            return 0;
904        }
905    }
906    uint32_t ref = ident;
907    if (resolve) {
908        block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config);
909        if (kThrowOnBadId) {
910            if (block == BAD_INDEX) {
911                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
912                return 0;
913            }
914        }
915    }
916    if (block >= 0) {
917        return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config);
918    }
919
920    return static_cast<jint>(block);
921}
922
923static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz,
924                                                           jint ident, jint bagEntryId,
925                                                           jobject outValue, jboolean resolve)
926{
927    AssetManager* am = assetManagerForJavaObject(env, clazz);
928    if (am == NULL) {
929        return 0;
930    }
931    const ResTable& res(am->getResources());
932
933    // Now lock down the resource object and start pulling stuff from it.
934    res.lock();
935
936    ssize_t block = -1;
937    Res_value value;
938
939    const ResTable::bag_entry* entry = NULL;
940    uint32_t typeSpecFlags;
941    ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags);
942
943    for (ssize_t i=0; i<entryCount; i++) {
944        if (((uint32_t)bagEntryId) == entry->map.name.ident) {
945            block = entry->stringBlock;
946            value = entry->map.value;
947        }
948        entry++;
949    }
950
951    res.unlock();
952
953    if (block < 0) {
954        return static_cast<jint>(block);
955    }
956
957    uint32_t ref = ident;
958    if (resolve) {
959        block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
960        if (kThrowOnBadId) {
961            if (block == BAD_INDEX) {
962                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
963                return 0;
964            }
965        }
966    }
967    if (block >= 0) {
968        return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags);
969    }
970
971    return static_cast<jint>(block);
972}
973
974static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz)
975{
976    AssetManager* am = assetManagerForJavaObject(env, clazz);
977    if (am == NULL) {
978        return 0;
979    }
980    return am->getResources().getTableCount();
981}
982
983static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz,
984                                                           jint block)
985{
986    AssetManager* am = assetManagerForJavaObject(env, clazz);
987    if (am == NULL) {
988        return 0;
989    }
990    return reinterpret_cast<jlong>(am->getResources().getTableStringBlock(block));
991}
992
993static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz,
994                                                       jint cookie)
995{
996    AssetManager* am = assetManagerForJavaObject(env, clazz);
997    if (am == NULL) {
998        return NULL;
999    }
1000    String8 name(am->getAssetPath(static_cast<int32_t>(cookie)));
1001    if (name.length() == 0) {
1002        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name");
1003        return NULL;
1004    }
1005    jstring str = env->NewStringUTF(name.string());
1006    return str;
1007}
1008
1009static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz)
1010{
1011    AssetManager* am = assetManagerForJavaObject(env, clazz);
1012    if (am == NULL) {
1013        return 0;
1014    }
1015
1016    const ResTable& res = am->getResources();
1017
1018    jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject,
1019            gSparseArrayOffsets.constructor);
1020    const size_t N = res.getBasePackageCount();
1021    for (size_t i = 0; i < N; i++) {
1022        const String16 name = res.getBasePackageName(i);
1023        env->CallVoidMethod(
1024            sparseArray, gSparseArrayOffsets.put,
1025            static_cast<jint>(res.getBasePackageId(i)),
1026            env->NewString(reinterpret_cast<const jchar*>(name.string()),
1027                           name.size()));
1028    }
1029    return sparseArray;
1030}
1031
1032static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz)
1033{
1034    AssetManager* am = assetManagerForJavaObject(env, clazz);
1035    if (am == NULL) {
1036        return 0;
1037    }
1038    return reinterpret_cast<jlong>(new ResTable::Theme(am->getResources()));
1039}
1040
1041static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz,
1042                                                     jlong themeHandle)
1043{
1044    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
1045    delete theme;
1046}
1047
1048static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz,
1049                                                         jlong themeHandle,
1050                                                         jint styleRes,
1051                                                         jboolean force)
1052{
1053    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
1054    theme->applyStyle(styleRes, force ? true : false);
1055}
1056
1057static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz,
1058                                                   jlong destHandle, jlong srcHandle)
1059{
1060    ResTable::Theme* dest = reinterpret_cast<ResTable::Theme*>(destHandle);
1061    ResTable::Theme* src = reinterpret_cast<ResTable::Theme*>(srcHandle);
1062    dest->setTo(*src);
1063}
1064
1065static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle)
1066{
1067    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
1068    theme->clear();
1069}
1070
1071static jint android_content_AssetManager_loadThemeAttributeValue(
1072    JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve)
1073{
1074    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
1075    const ResTable& res(theme->getResTable());
1076
1077    Res_value value;
1078    // XXX value could be different in different configs!
1079    uint32_t typeSpecFlags = 0;
1080    ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags);
1081    uint32_t ref = 0;
1082    if (resolve) {
1083        block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
1084        if (kThrowOnBadId) {
1085            if (block == BAD_INDEX) {
1086                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1087                return 0;
1088            }
1089        }
1090    }
1091    return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block;
1092}
1093
1094static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz,
1095                                                                        jlong themeHandle)
1096{
1097    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
1098    return theme->getChangingConfigurations();
1099}
1100
1101static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz,
1102                                                   jlong themeHandle, jint pri,
1103                                                   jstring tag, jstring prefix)
1104{
1105    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
1106    const ResTable& res(theme->getResTable());
1107    (void)res;
1108
1109    // XXX Need to use params.
1110    theme->dumpToLog();
1111}
1112
1113class XmlAttributeFinder : public BackTrackingAttributeFinder<XmlAttributeFinder, jsize> {
1114public:
1115    XmlAttributeFinder(const ResXMLParser* parser)
1116        : BackTrackingAttributeFinder(0, parser != NULL ? parser->getAttributeCount() : 0)
1117        , mParser(parser) {}
1118
1119    inline uint32_t getAttribute(jsize index) const {
1120        return mParser->getAttributeNameResID(index);
1121    }
1122
1123private:
1124    const ResXMLParser* mParser;
1125};
1126
1127class BagAttributeFinder : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
1128public:
1129    BagAttributeFinder(const ResTable::bag_entry* start, const ResTable::bag_entry* end)
1130        : BackTrackingAttributeFinder(start, end) {}
1131
1132    inline uint32_t getAttribute(const ResTable::bag_entry* entry) const {
1133        return entry->map.name.ident;
1134    }
1135};
1136
1137static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz,
1138                                                          jlong themeToken,
1139                                                          jint defStyleAttr,
1140                                                          jint defStyleRes,
1141                                                          jintArray inValues,
1142                                                          jintArray attrs,
1143                                                          jintArray outValues,
1144                                                          jintArray outIndices)
1145{
1146    if (themeToken == 0) {
1147        jniThrowNullPointerException(env, "theme token");
1148        return JNI_FALSE;
1149    }
1150    if (attrs == NULL) {
1151        jniThrowNullPointerException(env, "attrs");
1152        return JNI_FALSE;
1153    }
1154    if (outValues == NULL) {
1155        jniThrowNullPointerException(env, "out values");
1156        return JNI_FALSE;
1157    }
1158
1159    if (kDebugStyles) {
1160        ALOGI("APPLY STYLE: theme=0x%" PRIx64 " defStyleAttr=0x%x "
1161              "defStyleRes=0x%x", themeToken, defStyleAttr, defStyleRes);
1162    }
1163
1164    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
1165    const ResTable& res = theme->getResTable();
1166    ResTable_config config;
1167    Res_value value;
1168
1169    const jsize NI = env->GetArrayLength(attrs);
1170    const jsize NV = env->GetArrayLength(outValues);
1171    if (NV < (NI*STYLE_NUM_ENTRIES)) {
1172        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
1173        return JNI_FALSE;
1174    }
1175
1176    jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
1177    if (src == NULL) {
1178        return JNI_FALSE;
1179    }
1180
1181    jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0);
1182    const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues);
1183
1184    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1185    jint* dest = baseDest;
1186    if (dest == NULL) {
1187        env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1188        return JNI_FALSE;
1189    }
1190
1191    jint* indices = NULL;
1192    int indicesIdx = 0;
1193    if (outIndices != NULL) {
1194        if (env->GetArrayLength(outIndices) > NI) {
1195            indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
1196        }
1197    }
1198
1199    // Load default style from attribute, if specified...
1200    uint32_t defStyleBagTypeSetFlags = 0;
1201    if (defStyleAttr != 0) {
1202        Res_value value;
1203        if (theme->getAttribute(defStyleAttr, &value, &defStyleBagTypeSetFlags) >= 0) {
1204            if (value.dataType == Res_value::TYPE_REFERENCE) {
1205                defStyleRes = value.data;
1206            }
1207        }
1208    }
1209
1210    // Now lock down the resource object and start pulling stuff from it.
1211    res.lock();
1212
1213    // Retrieve the default style bag, if requested.
1214    const ResTable::bag_entry* defStyleStart = NULL;
1215    uint32_t defStyleTypeSetFlags = 0;
1216    ssize_t bagOff = defStyleRes != 0
1217            ? res.getBagLocked(defStyleRes, &defStyleStart, &defStyleTypeSetFlags) : -1;
1218    defStyleTypeSetFlags |= defStyleBagTypeSetFlags;
1219    const ResTable::bag_entry* const defStyleEnd = defStyleStart + (bagOff >= 0 ? bagOff : 0);
1220    BagAttributeFinder defStyleAttrFinder(defStyleStart, defStyleEnd);
1221
1222    // Now iterate through all of the attributes that the client has requested,
1223    // filling in each with whatever data we can find.
1224    ssize_t block = 0;
1225    uint32_t typeSetFlags;
1226    for (jsize ii=0; ii<NI; ii++) {
1227        const uint32_t curIdent = (uint32_t)src[ii];
1228
1229        if (kDebugStyles) {
1230            ALOGI("RETRIEVING ATTR 0x%08x...", curIdent);
1231        }
1232
1233        // Try to find a value for this attribute...  we prioritize values
1234        // coming from, first XML attributes, then XML style, then default
1235        // style, and finally the theme.
1236        value.dataType = Res_value::TYPE_NULL;
1237        value.data = Res_value::DATA_NULL_UNDEFINED;
1238        typeSetFlags = 0;
1239        config.density = 0;
1240
1241        // Retrieve the current input value if available.
1242        if (NSV > 0 && srcValues[ii] != 0) {
1243            block = -1;
1244            value.dataType = Res_value::TYPE_ATTRIBUTE;
1245            value.data = srcValues[ii];
1246            if (kDebugStyles) {
1247                ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
1248            }
1249        }
1250
1251        if (value.dataType == Res_value::TYPE_NULL) {
1252            const ResTable::bag_entry* const defStyleEntry = defStyleAttrFinder.find(curIdent);
1253            if (defStyleEntry != defStyleEnd) {
1254                block = defStyleEntry->stringBlock;
1255                typeSetFlags = defStyleTypeSetFlags;
1256                value = defStyleEntry->map.value;
1257                if (kDebugStyles) {
1258                    ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
1259                }
1260            }
1261        }
1262
1263        uint32_t resid = 0;
1264        if (value.dataType != Res_value::TYPE_NULL) {
1265            // Take care of resolving the found resource to its final value.
1266            ssize_t newBlock = theme->resolveAttributeReference(&value, block,
1267                    &resid, &typeSetFlags, &config);
1268            if (newBlock >= 0) block = newBlock;
1269            if (kDebugStyles) {
1270                ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
1271            }
1272        } else {
1273            // If we still don't have a value for this attribute, try to find
1274            // it in the theme!
1275            ssize_t newBlock = theme->getAttribute(curIdent, &value, &typeSetFlags);
1276            if (newBlock >= 0) {
1277                if (kDebugStyles) {
1278                    ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
1279                }
1280                newBlock = res.resolveReference(&value, block, &resid,
1281                        &typeSetFlags, &config);
1282                if (kThrowOnBadId) {
1283                    if (newBlock == BAD_INDEX) {
1284                        jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1285                        return JNI_FALSE;
1286                    }
1287                }
1288                if (newBlock >= 0) block = newBlock;
1289                if (kDebugStyles) {
1290                    ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
1291                }
1292            }
1293        }
1294
1295        // Deal with the special @null value -- it turns back to TYPE_NULL.
1296        if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
1297            if (kDebugStyles) {
1298                ALOGI("-> Setting to @null!");
1299            }
1300            value.dataType = Res_value::TYPE_NULL;
1301            value.data = Res_value::DATA_NULL_UNDEFINED;
1302            block = -1;
1303        }
1304
1305        if (kDebugStyles) {
1306            ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", curIdent, value.dataType,
1307                  value.data);
1308        }
1309
1310        // Write the final value back to Java.
1311        dest[STYLE_TYPE] = value.dataType;
1312        dest[STYLE_DATA] = value.data;
1313        dest[STYLE_ASSET_COOKIE] =
1314            block != -1 ? reinterpret_cast<jint>(res.getTableCookie(block)) : (jint)-1;
1315        dest[STYLE_RESOURCE_ID] = resid;
1316        dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
1317        dest[STYLE_DENSITY] = config.density;
1318
1319        if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
1320            indicesIdx++;
1321            indices[indicesIdx] = ii;
1322        }
1323
1324        dest += STYLE_NUM_ENTRIES;
1325    }
1326
1327    res.unlock();
1328
1329    if (indices != NULL) {
1330        indices[0] = indicesIdx;
1331        env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
1332    }
1333    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1334    env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0);
1335    env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1336
1337    return JNI_TRUE;
1338}
1339
1340static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject clazz,
1341                                                        jlong themeToken,
1342                                                        jint defStyleAttr,
1343                                                        jint defStyleRes,
1344                                                        jlong xmlParserToken,
1345                                                        jintArray attrs,
1346                                                        jintArray outValues,
1347                                                        jintArray outIndices)
1348{
1349    if (themeToken == 0) {
1350        jniThrowNullPointerException(env, "theme token");
1351        return JNI_FALSE;
1352    }
1353    if (attrs == NULL) {
1354        jniThrowNullPointerException(env, "attrs");
1355        return JNI_FALSE;
1356    }
1357    if (outValues == NULL) {
1358        jniThrowNullPointerException(env, "out values");
1359        return JNI_FALSE;
1360    }
1361
1362    if (kDebugStyles) {
1363    ALOGI("APPLY STYLE: theme=0x%" PRIx64 " defStyleAttr=0x%x defStyleRes=0x%x "
1364          "xml=0x%" PRIx64, themeToken, defStyleAttr, defStyleRes,
1365          xmlParserToken);
1366    }
1367
1368    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
1369    const ResTable& res = theme->getResTable();
1370    ResXMLParser* xmlParser = reinterpret_cast<ResXMLParser*>(xmlParserToken);
1371    ResTable_config config;
1372    Res_value value;
1373
1374    const jsize NI = env->GetArrayLength(attrs);
1375    const jsize NV = env->GetArrayLength(outValues);
1376    if (NV < (NI*STYLE_NUM_ENTRIES)) {
1377        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
1378        return JNI_FALSE;
1379    }
1380
1381    jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
1382    if (src == NULL) {
1383        return JNI_FALSE;
1384    }
1385
1386    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1387    jint* dest = baseDest;
1388    if (dest == NULL) {
1389        env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1390        return JNI_FALSE;
1391    }
1392
1393    jint* indices = NULL;
1394    int indicesIdx = 0;
1395    if (outIndices != NULL) {
1396        if (env->GetArrayLength(outIndices) > NI) {
1397            indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
1398        }
1399    }
1400
1401    // Load default style from attribute, if specified...
1402    uint32_t defStyleBagTypeSetFlags = 0;
1403    if (defStyleAttr != 0) {
1404        Res_value value;
1405        if (theme->getAttribute(defStyleAttr, &value, &defStyleBagTypeSetFlags) >= 0) {
1406            if (value.dataType == Res_value::TYPE_REFERENCE) {
1407                defStyleRes = value.data;
1408            }
1409        }
1410    }
1411
1412    // Retrieve the style class associated with the current XML tag.
1413    int style = 0;
1414    uint32_t styleBagTypeSetFlags = 0;
1415    if (xmlParser != NULL) {
1416        ssize_t idx = xmlParser->indexOfStyle();
1417        if (idx >= 0 && xmlParser->getAttributeValue(idx, &value) >= 0) {
1418            if (value.dataType == value.TYPE_ATTRIBUTE) {
1419                if (theme->getAttribute(value.data, &value, &styleBagTypeSetFlags) < 0) {
1420                    value.dataType = Res_value::TYPE_NULL;
1421                }
1422            }
1423            if (value.dataType == value.TYPE_REFERENCE) {
1424                style = value.data;
1425            }
1426        }
1427    }
1428
1429    // Now lock down the resource object and start pulling stuff from it.
1430    res.lock();
1431
1432    // Retrieve the default style bag, if requested.
1433    const ResTable::bag_entry* defStyleAttrStart = NULL;
1434    uint32_t defStyleTypeSetFlags = 0;
1435    ssize_t bagOff = defStyleRes != 0
1436            ? res.getBagLocked(defStyleRes, &defStyleAttrStart, &defStyleTypeSetFlags) : -1;
1437    defStyleTypeSetFlags |= defStyleBagTypeSetFlags;
1438    const ResTable::bag_entry* const defStyleAttrEnd = defStyleAttrStart + (bagOff >= 0 ? bagOff : 0);
1439    BagAttributeFinder defStyleAttrFinder(defStyleAttrStart, defStyleAttrEnd);
1440
1441    // Retrieve the style class bag, if requested.
1442    const ResTable::bag_entry* styleAttrStart = NULL;
1443    uint32_t styleTypeSetFlags = 0;
1444    bagOff = style != 0 ? res.getBagLocked(style, &styleAttrStart, &styleTypeSetFlags) : -1;
1445    styleTypeSetFlags |= styleBagTypeSetFlags;
1446    const ResTable::bag_entry* const styleAttrEnd = styleAttrStart + (bagOff >= 0 ? bagOff : 0);
1447    BagAttributeFinder styleAttrFinder(styleAttrStart, styleAttrEnd);
1448
1449    // Retrieve the XML attributes, if requested.
1450    static const ssize_t kXmlBlock = 0x10000000;
1451    XmlAttributeFinder xmlAttrFinder(xmlParser);
1452    const jsize xmlAttrEnd = xmlParser != NULL ? xmlParser->getAttributeCount() : 0;
1453
1454    // Now iterate through all of the attributes that the client has requested,
1455    // filling in each with whatever data we can find.
1456    ssize_t block = 0;
1457    uint32_t typeSetFlags;
1458    for (jsize ii = 0; ii < NI; ii++) {
1459        const uint32_t curIdent = (uint32_t)src[ii];
1460
1461        if (kDebugStyles) {
1462            ALOGI("RETRIEVING ATTR 0x%08x...", curIdent);
1463        }
1464
1465        // Try to find a value for this attribute...  we prioritize values
1466        // coming from, first XML attributes, then XML style, then default
1467        // style, and finally the theme.
1468        value.dataType = Res_value::TYPE_NULL;
1469        value.data = Res_value::DATA_NULL_UNDEFINED;
1470        typeSetFlags = 0;
1471        config.density = 0;
1472
1473        // Walk through the xml attributes looking for the requested attribute.
1474        const jsize xmlAttrIdx = xmlAttrFinder.find(curIdent);
1475        if (xmlAttrIdx != xmlAttrEnd) {
1476            // We found the attribute we were looking for.
1477            block = kXmlBlock;
1478            xmlParser->getAttributeValue(xmlAttrIdx, &value);
1479            if (kDebugStyles) {
1480                ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data);
1481            }
1482        }
1483
1484        if (value.dataType == Res_value::TYPE_NULL) {
1485            // Walk through the style class values looking for the requested attribute.
1486            const ResTable::bag_entry* const styleAttrEntry = styleAttrFinder.find(curIdent);
1487            if (styleAttrEntry != styleAttrEnd) {
1488                // We found the attribute we were looking for.
1489                block = styleAttrEntry->stringBlock;
1490                typeSetFlags = styleTypeSetFlags;
1491                value = styleAttrEntry->map.value;
1492                if (kDebugStyles) {
1493                    ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
1494                }
1495            }
1496        }
1497
1498        if (value.dataType == Res_value::TYPE_NULL) {
1499            // Walk through the default style values looking for the requested attribute.
1500            const ResTable::bag_entry* const defStyleAttrEntry = defStyleAttrFinder.find(curIdent);
1501            if (defStyleAttrEntry != defStyleAttrEnd) {
1502                // We found the attribute we were looking for.
1503                block = defStyleAttrEntry->stringBlock;
1504                typeSetFlags = styleTypeSetFlags;
1505                value = defStyleAttrEntry->map.value;
1506                if (kDebugStyles) {
1507                    ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
1508                }
1509            }
1510        }
1511
1512        uint32_t resid = 0;
1513        if (value.dataType != Res_value::TYPE_NULL) {
1514            // Take care of resolving the found resource to its final value.
1515            ssize_t newBlock = theme->resolveAttributeReference(&value, block,
1516                    &resid, &typeSetFlags, &config);
1517            if (newBlock >= 0) {
1518                block = newBlock;
1519            }
1520
1521            if (kDebugStyles) {
1522                ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
1523            }
1524        } else {
1525            // If we still don't have a value for this attribute, try to find
1526            // it in the theme!
1527            ssize_t newBlock = theme->getAttribute(curIdent, &value, &typeSetFlags);
1528            if (newBlock >= 0) {
1529                if (kDebugStyles) {
1530                    ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
1531                }
1532                newBlock = res.resolveReference(&value, block, &resid,
1533                        &typeSetFlags, &config);
1534                if (kThrowOnBadId) {
1535                    if (newBlock == BAD_INDEX) {
1536                        jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1537                        return JNI_FALSE;
1538                    }
1539                }
1540
1541                if (newBlock >= 0) {
1542                    block = newBlock;
1543                }
1544
1545                if (kDebugStyles) {
1546                    ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
1547                }
1548            }
1549        }
1550
1551        // Deal with the special @null value -- it turns back to TYPE_NULL.
1552        if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
1553            if (kDebugStyles) {
1554                ALOGI("-> Setting to @null!");
1555            }
1556            value.dataType = Res_value::TYPE_NULL;
1557            value.data = Res_value::DATA_NULL_UNDEFINED;
1558            block = kXmlBlock;
1559        }
1560
1561        if (kDebugStyles) {
1562            ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", curIdent, value.dataType, value.data);
1563        }
1564
1565        // Write the final value back to Java.
1566        dest[STYLE_TYPE] = value.dataType;
1567        dest[STYLE_DATA] = value.data;
1568        dest[STYLE_ASSET_COOKIE] = block != kXmlBlock ?
1569            static_cast<jint>(res.getTableCookie(block)) : -1;
1570        dest[STYLE_RESOURCE_ID] = resid;
1571        dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
1572        dest[STYLE_DENSITY] = config.density;
1573
1574        if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
1575            indicesIdx++;
1576            indices[indicesIdx] = ii;
1577        }
1578
1579        dest += STYLE_NUM_ENTRIES;
1580    }
1581
1582    res.unlock();
1583
1584    if (indices != NULL) {
1585        indices[0] = indicesIdx;
1586        env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
1587    }
1588    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1589    env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1590
1591    return JNI_TRUE;
1592}
1593
1594static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz,
1595                                                        jlong xmlParserToken,
1596                                                        jintArray attrs,
1597                                                        jintArray outValues,
1598                                                        jintArray outIndices)
1599{
1600    if (xmlParserToken == 0) {
1601        jniThrowNullPointerException(env, "xmlParserToken");
1602        return JNI_FALSE;
1603    }
1604    if (attrs == NULL) {
1605        jniThrowNullPointerException(env, "attrs");
1606        return JNI_FALSE;
1607    }
1608    if (outValues == NULL) {
1609        jniThrowNullPointerException(env, "out values");
1610        return JNI_FALSE;
1611    }
1612
1613    AssetManager* am = assetManagerForJavaObject(env, clazz);
1614    if (am == NULL) {
1615        return JNI_FALSE;
1616    }
1617    const ResTable& res(am->getResources());
1618    ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
1619    ResTable_config config;
1620    Res_value value;
1621
1622    const jsize NI = env->GetArrayLength(attrs);
1623    const jsize NV = env->GetArrayLength(outValues);
1624    if (NV < (NI*STYLE_NUM_ENTRIES)) {
1625        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
1626        return JNI_FALSE;
1627    }
1628
1629    jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
1630    if (src == NULL) {
1631        return JNI_FALSE;
1632    }
1633
1634    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1635    jint* dest = baseDest;
1636    if (dest == NULL) {
1637        env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1638        return JNI_FALSE;
1639    }
1640
1641    jint* indices = NULL;
1642    int indicesIdx = 0;
1643    if (outIndices != NULL) {
1644        if (env->GetArrayLength(outIndices) > NI) {
1645            indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
1646        }
1647    }
1648
1649    // Now lock down the resource object and start pulling stuff from it.
1650    res.lock();
1651
1652    // Retrieve the XML attributes, if requested.
1653    const jsize NX = xmlParser->getAttributeCount();
1654    jsize ix=0;
1655    uint32_t curXmlAttr = xmlParser->getAttributeNameResID(ix);
1656
1657    static const ssize_t kXmlBlock = 0x10000000;
1658
1659    // Now iterate through all of the attributes that the client has requested,
1660    // filling in each with whatever data we can find.
1661    ssize_t block = 0;
1662    uint32_t typeSetFlags;
1663    for (jsize ii=0; ii<NI; ii++) {
1664        const uint32_t curIdent = (uint32_t)src[ii];
1665
1666        // Try to find a value for this attribute...
1667        value.dataType = Res_value::TYPE_NULL;
1668        value.data = Res_value::DATA_NULL_UNDEFINED;
1669        typeSetFlags = 0;
1670        config.density = 0;
1671
1672        // Skip through XML attributes until the end or the next possible match.
1673        while (ix < NX && curIdent > curXmlAttr) {
1674            ix++;
1675            curXmlAttr = xmlParser->getAttributeNameResID(ix);
1676        }
1677        // Retrieve the current XML attribute if it matches, and step to next.
1678        if (ix < NX && curIdent == curXmlAttr) {
1679            block = kXmlBlock;
1680            xmlParser->getAttributeValue(ix, &value);
1681            ix++;
1682            curXmlAttr = xmlParser->getAttributeNameResID(ix);
1683        }
1684
1685        //printf("Attribute 0x%08x: type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
1686        uint32_t resid = 0;
1687        if (value.dataType != Res_value::TYPE_NULL) {
1688            // Take care of resolving the found resource to its final value.
1689            //printf("Resolving attribute reference\n");
1690            ssize_t newBlock = res.resolveReference(&value, block, &resid,
1691                    &typeSetFlags, &config);
1692            if (kThrowOnBadId) {
1693                if (newBlock == BAD_INDEX) {
1694                    jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1695                    return JNI_FALSE;
1696                }
1697            }
1698            if (newBlock >= 0) block = newBlock;
1699        }
1700
1701        // Deal with the special @null value -- it turns back to TYPE_NULL.
1702        if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
1703            value.dataType = Res_value::TYPE_NULL;
1704            value.data = Res_value::DATA_NULL_UNDEFINED;
1705        }
1706
1707        //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
1708
1709        // Write the final value back to Java.
1710        dest[STYLE_TYPE] = value.dataType;
1711        dest[STYLE_DATA] = value.data;
1712        dest[STYLE_ASSET_COOKIE] =
1713            block != kXmlBlock ? reinterpret_cast<jint>(res.getTableCookie(block)) : (jint)-1;
1714        dest[STYLE_RESOURCE_ID] = resid;
1715        dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
1716        dest[STYLE_DENSITY] = config.density;
1717
1718        if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
1719            indicesIdx++;
1720            indices[indicesIdx] = ii;
1721        }
1722
1723        dest += STYLE_NUM_ENTRIES;
1724    }
1725
1726    res.unlock();
1727
1728    if (indices != NULL) {
1729        indices[0] = indicesIdx;
1730        env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
1731    }
1732
1733    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1734    env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1735
1736    return JNI_TRUE;
1737}
1738
1739static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz,
1740                                                       jint id)
1741{
1742    AssetManager* am = assetManagerForJavaObject(env, clazz);
1743    if (am == NULL) {
1744        return 0;
1745    }
1746    const ResTable& res(am->getResources());
1747
1748    res.lock();
1749    const ResTable::bag_entry* defStyleEnt = NULL;
1750    ssize_t bagOff = res.getBagLocked(id, &defStyleEnt);
1751    res.unlock();
1752
1753    return static_cast<jint>(bagOff);
1754}
1755
1756static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz,
1757                                                        jint id,
1758                                                        jintArray outValues)
1759{
1760    if (outValues == NULL) {
1761        jniThrowNullPointerException(env, "out values");
1762        return JNI_FALSE;
1763    }
1764
1765    AssetManager* am = assetManagerForJavaObject(env, clazz);
1766    if (am == NULL) {
1767        return JNI_FALSE;
1768    }
1769    const ResTable& res(am->getResources());
1770    ResTable_config config;
1771    Res_value value;
1772    ssize_t block;
1773
1774    const jsize NV = env->GetArrayLength(outValues);
1775
1776    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1777    jint* dest = baseDest;
1778    if (dest == NULL) {
1779        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1780        return JNI_FALSE;
1781    }
1782
1783    // Now lock down the resource object and start pulling stuff from it.
1784    res.lock();
1785
1786    const ResTable::bag_entry* arrayEnt = NULL;
1787    uint32_t arrayTypeSetFlags = 0;
1788    ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags);
1789    const ResTable::bag_entry* endArrayEnt = arrayEnt +
1790        (bagOff >= 0 ? bagOff : 0);
1791
1792    int i = 0;
1793    uint32_t typeSetFlags;
1794    while (i < NV && arrayEnt < endArrayEnt) {
1795        block = arrayEnt->stringBlock;
1796        typeSetFlags = arrayTypeSetFlags;
1797        config.density = 0;
1798        value = arrayEnt->map.value;
1799
1800        uint32_t resid = 0;
1801        if (value.dataType != Res_value::TYPE_NULL) {
1802            // Take care of resolving the found resource to its final value.
1803            //printf("Resolving attribute reference\n");
1804            ssize_t newBlock = res.resolveReference(&value, block, &resid,
1805                    &typeSetFlags, &config);
1806            if (kThrowOnBadId) {
1807                if (newBlock == BAD_INDEX) {
1808                    jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1809                    return JNI_FALSE;
1810                }
1811            }
1812            if (newBlock >= 0) block = newBlock;
1813        }
1814
1815        // Deal with the special @null value -- it turns back to TYPE_NULL.
1816        if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
1817            value.dataType = Res_value::TYPE_NULL;
1818            value.data = Res_value::DATA_NULL_UNDEFINED;
1819        }
1820
1821        //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
1822
1823        // Write the final value back to Java.
1824        dest[STYLE_TYPE] = value.dataType;
1825        dest[STYLE_DATA] = value.data;
1826        dest[STYLE_ASSET_COOKIE] = reinterpret_cast<jint>(res.getTableCookie(block));
1827        dest[STYLE_RESOURCE_ID] = resid;
1828        dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
1829        dest[STYLE_DENSITY] = config.density;
1830        dest += STYLE_NUM_ENTRIES;
1831        i+= STYLE_NUM_ENTRIES;
1832        arrayEnt++;
1833    }
1834
1835    i /= STYLE_NUM_ENTRIES;
1836
1837    res.unlock();
1838
1839    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1840
1841    return i;
1842}
1843
1844static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz,
1845                                                         jint cookie,
1846                                                         jstring fileName)
1847{
1848    AssetManager* am = assetManagerForJavaObject(env, clazz);
1849    if (am == NULL) {
1850        return 0;
1851    }
1852
1853    ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz);
1854
1855    ScopedUtfChars fileName8(env, fileName);
1856    if (fileName8.c_str() == NULL) {
1857        return 0;
1858    }
1859
1860    int32_t assetCookie = static_cast<int32_t>(cookie);
1861    Asset* a = assetCookie
1862        ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER)
1863        : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie);
1864
1865    if (a == NULL) {
1866        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
1867        return 0;
1868    }
1869
1870    const DynamicRefTable* dynamicRefTable =
1871            am->getResources().getDynamicRefTableForCookie(assetCookie);
1872    ResXMLTree* block = new ResXMLTree(dynamicRefTable);
1873    status_t err = block->setTo(a->getBuffer(true), a->getLength(), true);
1874    a->close();
1875    delete a;
1876
1877    if (err != NO_ERROR) {
1878        jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
1879        return 0;
1880    }
1881
1882    return reinterpret_cast<jlong>(block);
1883}
1884
1885static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz,
1886                                                                 jint arrayResId)
1887{
1888    AssetManager* am = assetManagerForJavaObject(env, clazz);
1889    if (am == NULL) {
1890        return NULL;
1891    }
1892    const ResTable& res(am->getResources());
1893
1894    const ResTable::bag_entry* startOfBag;
1895    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1896    if (N < 0) {
1897        return NULL;
1898    }
1899
1900    jintArray array = env->NewIntArray(N * 2);
1901    if (array == NULL) {
1902        res.unlockBag(startOfBag);
1903        return NULL;
1904    }
1905
1906    Res_value value;
1907    const ResTable::bag_entry* bag = startOfBag;
1908    for (size_t i = 0, j = 0; ((ssize_t)i)<N; i++, bag++) {
1909        jint stringIndex = -1;
1910        jint stringBlock = 0;
1911        value = bag->map.value;
1912
1913        // Take care of resolving the found resource to its final value.
1914        stringBlock = res.resolveReference(&value, bag->stringBlock, NULL);
1915        if (value.dataType == Res_value::TYPE_STRING) {
1916            stringIndex = value.data;
1917        }
1918
1919        if (kThrowOnBadId) {
1920            if (stringBlock == BAD_INDEX) {
1921                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1922                return array;
1923            }
1924        }
1925
1926        //todo: It might be faster to allocate a C array to contain
1927        //      the blocknums and indices, put them in there and then
1928        //      do just one SetIntArrayRegion()
1929        env->SetIntArrayRegion(array, j, 1, &stringBlock);
1930        env->SetIntArrayRegion(array, j + 1, 1, &stringIndex);
1931        j = j + 2;
1932    }
1933    res.unlockBag(startOfBag);
1934    return array;
1935}
1936
1937static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz,
1938                                                                        jint arrayResId)
1939{
1940    AssetManager* am = assetManagerForJavaObject(env, clazz);
1941    if (am == NULL) {
1942        return NULL;
1943    }
1944    const ResTable& res(am->getResources());
1945
1946    const ResTable::bag_entry* startOfBag;
1947    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1948    if (N < 0) {
1949        return NULL;
1950    }
1951
1952    jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL);
1953    if (env->ExceptionCheck()) {
1954        res.unlockBag(startOfBag);
1955        return NULL;
1956    }
1957
1958    Res_value value;
1959    const ResTable::bag_entry* bag = startOfBag;
1960    size_t strLen = 0;
1961    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
1962        value = bag->map.value;
1963        jstring str = NULL;
1964
1965        // Take care of resolving the found resource to its final value.
1966        ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
1967        if (kThrowOnBadId) {
1968            if (block == BAD_INDEX) {
1969                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1970                return array;
1971            }
1972        }
1973        if (value.dataType == Res_value::TYPE_STRING) {
1974            const ResStringPool* pool = res.getTableStringBlock(block);
1975            const char* str8 = pool->string8At(value.data, &strLen);
1976            if (str8 != NULL) {
1977                str = env->NewStringUTF(str8);
1978            } else {
1979                const char16_t* str16 = pool->stringAt(value.data, &strLen);
1980                str = env->NewString(reinterpret_cast<const jchar*>(str16),
1981                                     strLen);
1982            }
1983
1984            // If one of our NewString{UTF} calls failed due to memory, an
1985            // exception will be pending.
1986            if (env->ExceptionCheck()) {
1987                res.unlockBag(startOfBag);
1988                return NULL;
1989            }
1990
1991            env->SetObjectArrayElement(array, i, str);
1992
1993            // str is not NULL at that point, otherwise ExceptionCheck would have been true.
1994            // If we have a large amount of strings in our array, we might
1995            // overflow the local reference table of the VM.
1996            env->DeleteLocalRef(str);
1997        }
1998    }
1999    res.unlockBag(startOfBag);
2000    return array;
2001}
2002
2003static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz,
2004                                                                        jint arrayResId)
2005{
2006    AssetManager* am = assetManagerForJavaObject(env, clazz);
2007    if (am == NULL) {
2008        return NULL;
2009    }
2010    const ResTable& res(am->getResources());
2011
2012    const ResTable::bag_entry* startOfBag;
2013    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
2014    if (N < 0) {
2015        return NULL;
2016    }
2017
2018    jintArray array = env->NewIntArray(N);
2019    if (array == NULL) {
2020        res.unlockBag(startOfBag);
2021        return NULL;
2022    }
2023
2024    Res_value value;
2025    const ResTable::bag_entry* bag = startOfBag;
2026    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
2027        value = bag->map.value;
2028
2029        // Take care of resolving the found resource to its final value.
2030        ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
2031        if (kThrowOnBadId) {
2032            if (block == BAD_INDEX) {
2033                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
2034                return array;
2035            }
2036        }
2037        if (value.dataType >= Res_value::TYPE_FIRST_INT
2038                && value.dataType <= Res_value::TYPE_LAST_INT) {
2039            int intVal = value.data;
2040            env->SetIntArrayRegion(array, i, 1, &intVal);
2041        }
2042    }
2043    res.unlockBag(startOfBag);
2044    return array;
2045}
2046
2047static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz,
2048                                                                 jint styleId)
2049{
2050    AssetManager* am = assetManagerForJavaObject(env, clazz);
2051    if (am == NULL) {
2052        return NULL;
2053    }
2054    const ResTable& res(am->getResources());
2055
2056    const ResTable::bag_entry* startOfBag;
2057    const ssize_t N = res.lockBag(styleId, &startOfBag);
2058    if (N < 0) {
2059        return NULL;
2060    }
2061
2062    jintArray array = env->NewIntArray(N);
2063    if (array == NULL) {
2064        res.unlockBag(startOfBag);
2065        return NULL;
2066    }
2067
2068    const ResTable::bag_entry* bag = startOfBag;
2069    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
2070        int resourceId = bag->map.name.ident;
2071        env->SetIntArrayRegion(array, i, 1, &resourceId);
2072    }
2073    res.unlockBag(startOfBag);
2074    return array;
2075}
2076
2077static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
2078{
2079    if (isSystem) {
2080        verifySystemIdmaps();
2081    }
2082    AssetManager* am = new AssetManager();
2083    if (am == NULL) {
2084        jniThrowException(env, "java/lang/OutOfMemoryError", "");
2085        return;
2086    }
2087
2088    am->addDefaultAssets();
2089
2090    ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
2091    env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am));
2092}
2093
2094static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz)
2095{
2096    AssetManager* am = (AssetManager*)
2097        (env->GetLongField(clazz, gAssetManagerOffsets.mObject));
2098    ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz);
2099    if (am != NULL) {
2100        delete am;
2101        env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0);
2102    }
2103}
2104
2105static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz)
2106{
2107    return Asset::getGlobalCount();
2108}
2109
2110static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz)
2111{
2112    String8 alloc = Asset::getAssetAllocations();
2113    if (alloc.length() <= 0) {
2114        return NULL;
2115    }
2116
2117    jstring str = env->NewStringUTF(alloc.string());
2118    return str;
2119}
2120
2121static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz)
2122{
2123    return AssetManager::getGlobalCount();
2124}
2125
2126// ----------------------------------------------------------------------------
2127
2128/*
2129 * JNI registration.
2130 */
2131static const JNINativeMethod gAssetManagerMethods[] = {
2132    /* name, signature, funcPtr */
2133
2134    // Basic asset stuff.
2135    { "openAsset",      "(Ljava/lang/String;I)J",
2136        (void*) android_content_AssetManager_openAsset },
2137    { "openAssetFd",      "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
2138        (void*) android_content_AssetManager_openAssetFd },
2139    { "openNonAssetNative", "(ILjava/lang/String;I)J",
2140        (void*) android_content_AssetManager_openNonAssetNative },
2141    { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
2142        (void*) android_content_AssetManager_openNonAssetFdNative },
2143    { "list",           "(Ljava/lang/String;)[Ljava/lang/String;",
2144        (void*) android_content_AssetManager_list },
2145    { "destroyAsset",   "(J)V",
2146        (void*) android_content_AssetManager_destroyAsset },
2147    { "readAssetChar",  "(J)I",
2148        (void*) android_content_AssetManager_readAssetChar },
2149    { "readAsset",      "(J[BII)I",
2150        (void*) android_content_AssetManager_readAsset },
2151    { "seekAsset",      "(JJI)J",
2152        (void*) android_content_AssetManager_seekAsset },
2153    { "getAssetLength", "!(J)J",
2154        (void*) android_content_AssetManager_getAssetLength },
2155    { "getAssetRemainingLength", "!(J)J",
2156        (void*) android_content_AssetManager_getAssetRemainingLength },
2157    { "addAssetPathNative", "(Ljava/lang/String;Z)I",
2158        (void*) android_content_AssetManager_addAssetPath },
2159    { "addOverlayPathNative",   "(Ljava/lang/String;)I",
2160        (void*) android_content_AssetManager_addOverlayPath },
2161    { "isUpToDate",     "()Z",
2162        (void*) android_content_AssetManager_isUpToDate },
2163
2164    // Resources.
2165    { "getLocales",      "()[Ljava/lang/String;",
2166        (void*) android_content_AssetManager_getLocales },
2167    { "getNonSystemLocales", "()[Ljava/lang/String;",
2168        (void*) android_content_AssetManager_getNonSystemLocales },
2169    { "getSizeConfigurations", "()[Landroid/content/res/Configuration;",
2170        (void*) android_content_AssetManager_getSizeConfigurations },
2171    { "setConfiguration", "!(IILjava/lang/String;IIIIIIIIIIIIII)V",
2172        (void*) android_content_AssetManager_setConfiguration },
2173    { "getResourceIdentifier","!(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
2174        (void*) android_content_AssetManager_getResourceIdentifier },
2175    { "getResourceName","!(I)Ljava/lang/String;",
2176        (void*) android_content_AssetManager_getResourceName },
2177    { "getResourcePackageName","!(I)Ljava/lang/String;",
2178        (void*) android_content_AssetManager_getResourcePackageName },
2179    { "getResourceTypeName","!(I)Ljava/lang/String;",
2180        (void*) android_content_AssetManager_getResourceTypeName },
2181    { "getResourceEntryName","!(I)Ljava/lang/String;",
2182        (void*) android_content_AssetManager_getResourceEntryName },
2183    { "loadResourceValue","!(ISLandroid/util/TypedValue;Z)I",
2184        (void*) android_content_AssetManager_loadResourceValue },
2185    { "loadResourceBagValue","!(IILandroid/util/TypedValue;Z)I",
2186        (void*) android_content_AssetManager_loadResourceBagValue },
2187    { "getStringBlockCount","!()I",
2188        (void*) android_content_AssetManager_getStringBlockCount },
2189    { "getNativeStringBlock","!(I)J",
2190        (void*) android_content_AssetManager_getNativeStringBlock },
2191    { "getCookieName","(I)Ljava/lang/String;",
2192        (void*) android_content_AssetManager_getCookieName },
2193    { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;",
2194        (void*) android_content_AssetManager_getAssignedPackageIdentifiers },
2195
2196    // Themes.
2197    { "newTheme", "()J",
2198        (void*) android_content_AssetManager_newTheme },
2199    { "deleteTheme", "(J)V",
2200        (void*) android_content_AssetManager_deleteTheme },
2201    { "applyThemeStyle", "(JIZ)V",
2202        (void*) android_content_AssetManager_applyThemeStyle },
2203    { "copyTheme", "(JJ)V",
2204        (void*) android_content_AssetManager_copyTheme },
2205    { "clearTheme", "(J)V",
2206        (void*) android_content_AssetManager_clearTheme },
2207    { "loadThemeAttributeValue", "!(JILandroid/util/TypedValue;Z)I",
2208        (void*) android_content_AssetManager_loadThemeAttributeValue },
2209    { "getThemeChangingConfigurations", "!(J)I",
2210        (void*) android_content_AssetManager_getThemeChangingConfigurations },
2211    { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V",
2212        (void*) android_content_AssetManager_dumpTheme },
2213    { "applyStyle","!(JIIJ[I[I[I)Z",
2214        (void*) android_content_AssetManager_applyStyle },
2215    { "resolveAttrs","!(JII[I[I[I[I)Z",
2216        (void*) android_content_AssetManager_resolveAttrs },
2217    { "retrieveAttributes","!(J[I[I[I)Z",
2218        (void*) android_content_AssetManager_retrieveAttributes },
2219    { "getArraySize","!(I)I",
2220        (void*) android_content_AssetManager_getArraySize },
2221    { "retrieveArray","!(I[I)I",
2222        (void*) android_content_AssetManager_retrieveArray },
2223
2224    // XML files.
2225    { "openXmlAssetNative", "(ILjava/lang/String;)J",
2226        (void*) android_content_AssetManager_openXmlAssetNative },
2227
2228    // Arrays.
2229    { "getArrayStringResource","(I)[Ljava/lang/String;",
2230        (void*) android_content_AssetManager_getArrayStringResource },
2231    { "getArrayStringInfo","!(I)[I",
2232        (void*) android_content_AssetManager_getArrayStringInfo },
2233    { "getArrayIntResource","!(I)[I",
2234        (void*) android_content_AssetManager_getArrayIntResource },
2235    { "getStyleAttributes","!(I)[I",
2236        (void*) android_content_AssetManager_getStyleAttributes },
2237
2238    // Bookkeeping.
2239    { "init",           "(Z)V",
2240        (void*) android_content_AssetManager_init },
2241    { "destroy",        "()V",
2242        (void*) android_content_AssetManager_destroy },
2243    { "getGlobalAssetCount", "()I",
2244        (void*) android_content_AssetManager_getGlobalAssetCount },
2245    { "getAssetAllocations", "()Ljava/lang/String;",
2246        (void*) android_content_AssetManager_getAssetAllocations },
2247    { "getGlobalAssetManagerCount", "()I",
2248        (void*) android_content_AssetManager_getGlobalAssetManagerCount },
2249};
2250
2251int register_android_content_AssetManager(JNIEnv* env)
2252{
2253    jclass typedValue = FindClassOrDie(env, "android/util/TypedValue");
2254    gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I");
2255    gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I");
2256    gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string",
2257                                                 "Ljava/lang/CharSequence;");
2258    gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I");
2259    gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I");
2260    gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue,
2261                                                                 "changingConfigurations", "I");
2262    gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
2263
2264    jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
2265    gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd",
2266                                                      "Landroid/os/ParcelFileDescriptor;");
2267    gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
2268    gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
2269
2270    jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
2271    gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
2272
2273    jclass stringClass = FindClassOrDie(env, "java/lang/String");
2274    g_stringClass = MakeGlobalRefOrDie(env, stringClass);
2275
2276    jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
2277    gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
2278    gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject,
2279                                                       "<init>", "()V");
2280    gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put",
2281                                               "(ILjava/lang/Object;)V");
2282
2283    jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
2284    gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
2285    gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass,
2286            "<init>", "()V");
2287    gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
2288            "smallestScreenWidthDp", "I");
2289    gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
2290            "screenWidthDp", "I");
2291    gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass,
2292            "screenHeightDp", "I");
2293
2294    return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
2295                                NELEM(gAssetManagerMethods));
2296}
2297
2298}; // namespace android
2299