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#include <sys/system_properties.h>
29
30#include <private/android_filesystem_config.h> // for AID_SYSTEM
31
32#include "androidfw/Asset.h"
33#include "androidfw/AssetManager.h"
34#include "androidfw/AttributeResolution.h"
35#include "androidfw/ResourceTypes.h"
36#include "android_runtime/AndroidRuntime.h"
37#include "android_util_Binder.h"
38#include "core_jni_helpers.h"
39#include "jni.h"
40#include <nativehelper/JNIHelp.h>
41#include <nativehelper/ScopedStringChars.h>
42#include <nativehelper/ScopedUtfChars.h>
43#include "utils/Log.h"
44#include "utils/misc.h"
45#include "utils/String8.h"
46
47extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
48extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
49
50
51namespace android {
52
53static const bool kThrowOnBadId = false;
54
55// ----------------------------------------------------------------------------
56
57static struct typedvalue_offsets_t
58{
59    jfieldID mType;
60    jfieldID mData;
61    jfieldID mString;
62    jfieldID mAssetCookie;
63    jfieldID mResourceId;
64    jfieldID mChangingConfigurations;
65    jfieldID mDensity;
66} gTypedValueOffsets;
67
68static struct assetfiledescriptor_offsets_t
69{
70    jfieldID mFd;
71    jfieldID mStartOffset;
72    jfieldID mLength;
73} gAssetFileDescriptorOffsets;
74
75static struct assetmanager_offsets_t
76{
77    jfieldID mObject;
78} gAssetManagerOffsets;
79
80static struct sparsearray_offsets_t
81{
82    jclass classObject;
83    jmethodID constructor;
84    jmethodID put;
85} gSparseArrayOffsets;
86
87static struct configuration_offsets_t
88{
89    jclass classObject;
90    jmethodID constructor;
91    jfieldID mSmallestScreenWidthDpOffset;
92    jfieldID mScreenWidthDpOffset;
93    jfieldID mScreenHeightDpOffset;
94} gConfigurationOffsets;
95
96jclass g_stringClass = NULL;
97
98// ----------------------------------------------------------------------------
99
100static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
101                      const Res_value& value, uint32_t ref, ssize_t block,
102                      uint32_t typeSpecFlags, ResTable_config* config = NULL);
103
104jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
105               const Res_value& value, uint32_t ref, ssize_t block,
106               uint32_t typeSpecFlags, ResTable_config* config)
107{
108    env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType);
109    env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie,
110                     static_cast<jint>(table->getTableCookie(block)));
111    env->SetIntField(outValue, gTypedValueOffsets.mData, value.data);
112    env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL);
113    env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref);
114    env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations,
115            typeSpecFlags);
116    if (config != NULL) {
117        env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density);
118    }
119    return block;
120}
121
122// This is called by zygote (running as user root) as part of preloadResources.
123static void verifySystemIdmaps()
124{
125    pid_t pid;
126    char system_id[10];
127
128    snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM);
129
130    switch (pid = fork()) {
131        case -1:
132            ALOGE("failed to fork for idmap: %s", strerror(errno));
133            break;
134        case 0: // child
135            {
136                struct __user_cap_header_struct capheader;
137                struct __user_cap_data_struct capdata;
138
139                memset(&capheader, 0, sizeof(capheader));
140                memset(&capdata, 0, sizeof(capdata));
141
142                capheader.version = _LINUX_CAPABILITY_VERSION;
143                capheader.pid = 0;
144
145                if (capget(&capheader, &capdata) != 0) {
146                    ALOGE("capget: %s\n", strerror(errno));
147                    exit(1);
148                }
149
150                capdata.effective = capdata.permitted;
151                if (capset(&capheader, &capdata) != 0) {
152                    ALOGE("capset: %s\n", strerror(errno));
153                    exit(1);
154                }
155
156                if (setgid(AID_SYSTEM) != 0) {
157                    ALOGE("setgid: %s\n", strerror(errno));
158                    exit(1);
159                }
160
161                if (setuid(AID_SYSTEM) != 0) {
162                    ALOGE("setuid: %s\n", strerror(errno));
163                    exit(1);
164                }
165
166                // Generic idmap parameters
167                const char* argv[8];
168                int argc = 0;
169                struct stat st;
170
171                memset(argv, NULL, sizeof(argv));
172                argv[argc++] = AssetManager::IDMAP_BIN;
173                argv[argc++] = "--scan";
174                argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
175                argv[argc++] = AssetManager::TARGET_APK_PATH;
176                argv[argc++] = AssetManager::IDMAP_DIR;
177
178                // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
179                // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
180                char subdir[PROP_VALUE_MAX];
181                int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir);
182                if (len > 0) {
183                    String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir;
184                    if (stat(overlayPath.string(), &st) == 0) {
185                        argv[argc++] = overlayPath.string();
186                    }
187                }
188                if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
189                    argv[argc++] = AssetManager::OVERLAY_DIR;
190                }
191
192                // Finally, invoke idmap (if any overlay directory exists)
193                if (argc > 5) {
194                    execv(AssetManager::IDMAP_BIN, (char* const*)argv);
195                    ALOGE("failed to execv for idmap: %s", strerror(errno));
196                    exit(1); // should never get here
197                } else {
198                    exit(0);
199                }
200            }
201            break;
202        default: // parent
203            waitpid(pid, NULL, 0);
204            break;
205    }
206}
207
208// ----------------------------------------------------------------------------
209
210// this guy is exported to other jni routines
211AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj)
212{
213    jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject);
214    AssetManager* am = reinterpret_cast<AssetManager*>(amHandle);
215    if (am != NULL) {
216        return am;
217    }
218    jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
219    return NULL;
220}
221
222static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz,
223                                                jstring fileName, jint mode)
224{
225    AssetManager* am = assetManagerForJavaObject(env, clazz);
226    if (am == NULL) {
227        return 0;
228    }
229
230    ALOGV("openAsset in %p (Java object %p)\n", am, clazz);
231
232    ScopedUtfChars fileName8(env, fileName);
233    if (fileName8.c_str() == NULL) {
234        jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name");
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        jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
241        return -1;
242    }
243
244    Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode);
245
246    if (a == NULL) {
247        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
248        return -1;
249    }
250
251    //printf("Created Asset Stream: %p\n", a);
252
253    return reinterpret_cast<jlong>(a);
254}
255
256static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets)
257{
258    off64_t startOffset, length;
259    int fd = a->openFileDescriptor(&startOffset, &length);
260    delete a;
261
262    if (fd < 0) {
263        jniThrowException(env, "java/io/FileNotFoundException",
264                "This file can not be opened as a file descriptor; it is probably compressed");
265        return NULL;
266    }
267
268    jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0);
269    if (offsets == NULL) {
270        close(fd);
271        return NULL;
272    }
273
274    offsets[0] = startOffset;
275    offsets[1] = length;
276
277    env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0);
278
279    jobject fileDesc = jniCreateFileDescriptor(env, fd);
280    if (fileDesc == NULL) {
281        close(fd);
282        return NULL;
283    }
284
285    return newParcelFileDescriptor(env, fileDesc);
286}
287
288static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz,
289                                                jstring fileName, jlongArray outOffsets)
290{
291    AssetManager* am = assetManagerForJavaObject(env, clazz);
292    if (am == NULL) {
293        return NULL;
294    }
295
296    ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz);
297
298    ScopedUtfChars fileName8(env, fileName);
299    if (fileName8.c_str() == NULL) {
300        return NULL;
301    }
302
303    Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM);
304
305    if (a == NULL) {
306        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
307        return NULL;
308    }
309
310    //printf("Created Asset Stream: %p\n", a);
311
312    return returnParcelFileDescriptor(env, a, outOffsets);
313}
314
315static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz,
316                                                         jint cookie,
317                                                         jstring fileName,
318                                                         jint mode)
319{
320    AssetManager* am = assetManagerForJavaObject(env, clazz);
321    if (am == NULL) {
322        return 0;
323    }
324
325    ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz);
326
327    ScopedUtfChars fileName8(env, fileName);
328    if (fileName8.c_str() == NULL) {
329        return -1;
330    }
331
332    if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
333        && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
334        jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
335        return -1;
336    }
337
338    Asset* a = cookie
339        ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(),
340                (Asset::AccessMode)mode)
341        : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode);
342
343    if (a == NULL) {
344        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
345        return -1;
346    }
347
348    //printf("Created Asset Stream: %p\n", a);
349
350    return reinterpret_cast<jlong>(a);
351}
352
353static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz,
354                                                         jint cookie,
355                                                         jstring fileName,
356                                                         jlongArray outOffsets)
357{
358    AssetManager* am = assetManagerForJavaObject(env, clazz);
359    if (am == NULL) {
360        return NULL;
361    }
362
363    ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz);
364
365    ScopedUtfChars fileName8(env, fileName);
366    if (fileName8.c_str() == NULL) {
367        return NULL;
368    }
369
370    Asset* a = cookie
371        ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM)
372        : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM);
373
374    if (a == NULL) {
375        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
376        return NULL;
377    }
378
379    //printf("Created Asset Stream: %p\n", a);
380
381    return returnParcelFileDescriptor(env, a, outOffsets);
382}
383
384static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz,
385                                                   jstring fileName)
386{
387    AssetManager* am = assetManagerForJavaObject(env, clazz);
388    if (am == NULL) {
389        return NULL;
390    }
391
392    ScopedUtfChars fileName8(env, fileName);
393    if (fileName8.c_str() == NULL) {
394        return NULL;
395    }
396
397    AssetDir* dir = am->openDir(fileName8.c_str());
398
399    if (dir == NULL) {
400        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
401        return NULL;
402    }
403
404    size_t N = dir->getFileCount();
405
406    jobjectArray array = env->NewObjectArray(dir->getFileCount(),
407                                                g_stringClass, NULL);
408    if (array == NULL) {
409        delete dir;
410        return NULL;
411    }
412
413    for (size_t i=0; i<N; i++) {
414        const String8& name = dir->getFileName(i);
415        jstring str = env->NewStringUTF(name.string());
416        if (str == NULL) {
417            delete dir;
418            return NULL;
419        }
420        env->SetObjectArrayElement(array, i, str);
421        env->DeleteLocalRef(str);
422    }
423
424    delete dir;
425
426    return array;
427}
428
429static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz,
430                                                      jlong assetHandle)
431{
432    Asset* a = reinterpret_cast<Asset*>(assetHandle);
433
434    //printf("Destroying Asset Stream: %p\n", a);
435
436    if (a == NULL) {
437        jniThrowNullPointerException(env, "asset");
438        return;
439    }
440
441    delete a;
442}
443
444static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz,
445                                                       jlong assetHandle)
446{
447    Asset* a = reinterpret_cast<Asset*>(assetHandle);
448
449    if (a == NULL) {
450        jniThrowNullPointerException(env, "asset");
451        return -1;
452    }
453
454    uint8_t b;
455    ssize_t res = a->read(&b, 1);
456    return res == 1 ? b : -1;
457}
458
459static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz,
460                                                jlong assetHandle, jbyteArray bArray,
461                                                jint off, jint len)
462{
463    Asset* a = reinterpret_cast<Asset*>(assetHandle);
464
465    if (a == NULL || bArray == NULL) {
466        jniThrowNullPointerException(env, "asset");
467        return -1;
468    }
469
470    if (len == 0) {
471        return 0;
472    }
473
474    jsize bLen = env->GetArrayLength(bArray);
475    if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) {
476        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
477        return -1;
478    }
479
480    jbyte* b = env->GetByteArrayElements(bArray, NULL);
481    ssize_t res = a->read(b+off, len);
482    env->ReleaseByteArrayElements(bArray, b, 0);
483
484    if (res > 0) return static_cast<jint>(res);
485
486    if (res < 0) {
487        jniThrowException(env, "java/io/IOException", "");
488    }
489    return -1;
490}
491
492static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz,
493                                                 jlong assetHandle,
494                                                 jlong offset, jint whence)
495{
496    Asset* a = reinterpret_cast<Asset*>(assetHandle);
497
498    if (a == NULL) {
499        jniThrowNullPointerException(env, "asset");
500        return -1;
501    }
502
503    return a->seek(
504        offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR));
505}
506
507static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz,
508                                                      jlong assetHandle)
509{
510    Asset* a = reinterpret_cast<Asset*>(assetHandle);
511
512    if (a == NULL) {
513        jniThrowNullPointerException(env, "asset");
514        return -1;
515    }
516
517    return a->getLength();
518}
519
520static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz,
521                                                               jlong assetHandle)
522{
523    Asset* a = reinterpret_cast<Asset*>(assetHandle);
524
525    if (a == NULL) {
526        jniThrowNullPointerException(env, "asset");
527        return -1;
528    }
529
530    return a->getRemainingLength();
531}
532
533static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,
534                                                       jstring path, jboolean appAsLib)
535{
536    ScopedUtfChars path8(env, path);
537    if (path8.c_str() == NULL) {
538        return 0;
539    }
540
541    AssetManager* am = assetManagerForJavaObject(env, clazz);
542    if (am == NULL) {
543        return 0;
544    }
545
546    int32_t cookie;
547    bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib);
548
549    return (res) ? static_cast<jint>(cookie) : 0;
550}
551
552static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz,
553                                                     jstring idmapPath)
554{
555    ScopedUtfChars idmapPath8(env, idmapPath);
556    if (idmapPath8.c_str() == NULL) {
557        return 0;
558    }
559
560    AssetManager* am = assetManagerForJavaObject(env, clazz);
561    if (am == NULL) {
562        return 0;
563    }
564
565    int32_t cookie;
566    bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie);
567
568    return (res) ? (jint)cookie : 0;
569}
570
571static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz)
572{
573    AssetManager* am = assetManagerForJavaObject(env, clazz);
574    if (am == NULL) {
575        return JNI_TRUE;
576    }
577    return am->isUpToDate() ? JNI_TRUE : JNI_FALSE;
578}
579
580static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales)
581{
582    Vector<String8> locales;
583
584    AssetManager* am = assetManagerForJavaObject(env, clazz);
585    if (am == NULL) {
586        return NULL;
587    }
588
589    am->getLocales(&locales, includeSystemLocales);
590
591    const int N = locales.size();
592
593    jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL);
594    if (result == NULL) {
595        return NULL;
596    }
597
598    for (int i=0; i<N; i++) {
599        jstring str = env->NewStringUTF(locales[i].string());
600        if (str == NULL) {
601            return NULL;
602        }
603        env->SetObjectArrayElement(result, i, str);
604        env->DeleteLocalRef(str);
605    }
606
607    return result;
608}
609
610static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
611{
612    return getLocales(env, clazz, true /* include system locales */);
613}
614
615static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz)
616{
617    return getLocales(env, clazz, false /* don't include system locales */);
618}
619
620static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
621    jobject result = env->NewObject(gConfigurationOffsets.classObject,
622            gConfigurationOffsets.constructor);
623    if (result == NULL) {
624        return NULL;
625    }
626
627    env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset,
628            config.smallestScreenWidthDp);
629    env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
630    env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
631
632    return result;
633}
634
635static jobjectArray getSizeConfigurationsInternal(JNIEnv* env,
636        const Vector<ResTable_config>& configs) {
637    const int N = configs.size();
638    jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL);
639    if (result == NULL) {
640        return NULL;
641    }
642
643    for (int i=0; i<N; i++) {
644        jobject config = constructConfigurationObject(env, configs[i]);
645        if (config == NULL) {
646            env->DeleteLocalRef(result);
647            return NULL;
648        }
649
650        env->SetObjectArrayElement(result, i, config);
651        env->DeleteLocalRef(config);
652    }
653
654    return result;
655}
656
657static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) {
658    AssetManager* am = assetManagerForJavaObject(env, clazz);
659    if (am == NULL) {
660        return NULL;
661    }
662
663    const ResTable& res(am->getResources());
664    Vector<ResTable_config> configs;
665    res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */);
666
667    return getSizeConfigurationsInternal(env, configs);
668}
669
670static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz,
671                                                          jint mcc, jint mnc,
672                                                          jstring locale, jint orientation,
673                                                          jint touchscreen, jint density,
674                                                          jint keyboard, jint keyboardHidden,
675                                                          jint navigation,
676                                                          jint screenWidth, jint screenHeight,
677                                                          jint smallestScreenWidthDp,
678                                                          jint screenWidthDp, jint screenHeightDp,
679                                                          jint screenLayout, jint uiMode,
680                                                          jint colorMode, jint sdkVersion)
681{
682    AssetManager* am = assetManagerForJavaObject(env, clazz);
683    if (am == NULL) {
684        return;
685    }
686
687    ResTable_config config;
688    memset(&config, 0, sizeof(config));
689
690    const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL;
691
692    // Constants duplicated from Java class android.content.res.Configuration.
693    static const jint kScreenLayoutRoundMask = 0x300;
694    static const jint kScreenLayoutRoundShift = 8;
695
696    config.mcc = (uint16_t)mcc;
697    config.mnc = (uint16_t)mnc;
698    config.orientation = (uint8_t)orientation;
699    config.touchscreen = (uint8_t)touchscreen;
700    config.density = (uint16_t)density;
701    config.keyboard = (uint8_t)keyboard;
702    config.inputFlags = (uint8_t)keyboardHidden;
703    config.navigation = (uint8_t)navigation;
704    config.screenWidth = (uint16_t)screenWidth;
705    config.screenHeight = (uint16_t)screenHeight;
706    config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp;
707    config.screenWidthDp = (uint16_t)screenWidthDp;
708    config.screenHeightDp = (uint16_t)screenHeightDp;
709    config.screenLayout = (uint8_t)screenLayout;
710    config.uiMode = (uint8_t)uiMode;
711    config.colorMode = (uint8_t)colorMode;
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
1113static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz,
1114                                                          jlong themeToken,
1115                                                          jint defStyleAttr,
1116                                                          jint defStyleRes,
1117                                                          jintArray inValues,
1118                                                          jintArray attrs,
1119                                                          jintArray outValues,
1120                                                          jintArray outIndices)
1121{
1122    if (themeToken == 0) {
1123        jniThrowNullPointerException(env, "theme token");
1124        return JNI_FALSE;
1125    }
1126    if (attrs == NULL) {
1127        jniThrowNullPointerException(env, "attrs");
1128        return JNI_FALSE;
1129    }
1130    if (outValues == NULL) {
1131        jniThrowNullPointerException(env, "out values");
1132        return JNI_FALSE;
1133    }
1134
1135    const jsize NI = env->GetArrayLength(attrs);
1136    const jsize NV = env->GetArrayLength(outValues);
1137    if (NV < (NI*STYLE_NUM_ENTRIES)) {
1138        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
1139        return JNI_FALSE;
1140    }
1141
1142    jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
1143    if (src == NULL) {
1144        return JNI_FALSE;
1145    }
1146
1147    jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0);
1148    const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues);
1149
1150    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1151    if (baseDest == NULL) {
1152        env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1153        return JNI_FALSE;
1154    }
1155
1156    jint* indices = NULL;
1157    if (outIndices != NULL) {
1158        if (env->GetArrayLength(outIndices) > NI) {
1159            indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
1160        }
1161    }
1162
1163    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
1164    bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes,
1165                               (uint32_t*) srcValues, NSV,
1166                               (uint32_t*) src, NI,
1167                               (uint32_t*) baseDest,
1168                               (uint32_t*) indices);
1169
1170    if (indices != NULL) {
1171        env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
1172    }
1173    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1174    env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0);
1175    env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1176    return result ? JNI_TRUE : JNI_FALSE;
1177}
1178
1179static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken,
1180        jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length,
1181        jlong outValuesAddress, jlong outIndicesAddress) {
1182    jint* attrs = env->GetIntArrayElements(attrsObj, 0);
1183    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
1184    ResXMLParser* xmlParser = reinterpret_cast<ResXMLParser*>(xmlParserToken);
1185    uint32_t* outValues = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outValuesAddress));
1186    uint32_t* outIndices = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outIndicesAddress));
1187    ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes,
1188            reinterpret_cast<const uint32_t*>(attrs), length, outValues, outIndices);
1189    env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT);
1190}
1191
1192static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz,
1193                                                        jlong xmlParserToken,
1194                                                        jintArray attrs,
1195                                                        jintArray outValues,
1196                                                        jintArray outIndices)
1197{
1198    if (xmlParserToken == 0) {
1199        jniThrowNullPointerException(env, "xmlParserToken");
1200        return JNI_FALSE;
1201    }
1202    if (attrs == NULL) {
1203        jniThrowNullPointerException(env, "attrs");
1204        return JNI_FALSE;
1205    }
1206    if (outValues == NULL) {
1207        jniThrowNullPointerException(env, "out values");
1208        return JNI_FALSE;
1209    }
1210
1211    AssetManager* am = assetManagerForJavaObject(env, clazz);
1212    if (am == NULL) {
1213        return JNI_FALSE;
1214    }
1215    const ResTable& res(am->getResources());
1216    ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
1217
1218    const jsize NI = env->GetArrayLength(attrs);
1219    const jsize NV = env->GetArrayLength(outValues);
1220    if (NV < (NI*STYLE_NUM_ENTRIES)) {
1221        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
1222        return JNI_FALSE;
1223    }
1224
1225    jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
1226    if (src == NULL) {
1227        return JNI_FALSE;
1228    }
1229
1230    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1231    if (baseDest == NULL) {
1232        env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1233        return JNI_FALSE;
1234    }
1235
1236    jint* indices = NULL;
1237    if (outIndices != NULL) {
1238        if (env->GetArrayLength(outIndices) > NI) {
1239            indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
1240        }
1241    }
1242
1243    bool result = RetrieveAttributes(&res, xmlParser,
1244                                     (uint32_t*) src, NI,
1245                                     (uint32_t*) baseDest,
1246                                     (uint32_t*) indices);
1247
1248    if (indices != NULL) {
1249        env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
1250    }
1251    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1252    env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1253    return result ? JNI_TRUE : JNI_FALSE;
1254}
1255
1256static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz,
1257                                                       jint id)
1258{
1259    AssetManager* am = assetManagerForJavaObject(env, clazz);
1260    if (am == NULL) {
1261        return 0;
1262    }
1263    const ResTable& res(am->getResources());
1264
1265    res.lock();
1266    const ResTable::bag_entry* defStyleEnt = NULL;
1267    ssize_t bagOff = res.getBagLocked(id, &defStyleEnt);
1268    res.unlock();
1269
1270    return static_cast<jint>(bagOff);
1271}
1272
1273static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz,
1274                                                        jint id,
1275                                                        jintArray outValues)
1276{
1277    if (outValues == NULL) {
1278        jniThrowNullPointerException(env, "out values");
1279        return JNI_FALSE;
1280    }
1281
1282    AssetManager* am = assetManagerForJavaObject(env, clazz);
1283    if (am == NULL) {
1284        return JNI_FALSE;
1285    }
1286    const ResTable& res(am->getResources());
1287    ResTable_config config;
1288    Res_value value;
1289    ssize_t block;
1290
1291    const jsize NV = env->GetArrayLength(outValues);
1292
1293    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1294    jint* dest = baseDest;
1295    if (dest == NULL) {
1296        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1297        return JNI_FALSE;
1298    }
1299
1300    // Now lock down the resource object and start pulling stuff from it.
1301    res.lock();
1302
1303    const ResTable::bag_entry* arrayEnt = NULL;
1304    uint32_t arrayTypeSetFlags = 0;
1305    ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags);
1306    const ResTable::bag_entry* endArrayEnt = arrayEnt +
1307        (bagOff >= 0 ? bagOff : 0);
1308
1309    int i = 0;
1310    uint32_t typeSetFlags;
1311    while (i < NV && arrayEnt < endArrayEnt) {
1312        block = arrayEnt->stringBlock;
1313        typeSetFlags = arrayTypeSetFlags;
1314        config.density = 0;
1315        value = arrayEnt->map.value;
1316
1317        uint32_t resid = 0;
1318        if (value.dataType != Res_value::TYPE_NULL) {
1319            // Take care of resolving the found resource to its final value.
1320            //printf("Resolving attribute reference\n");
1321            ssize_t newBlock = res.resolveReference(&value, block, &resid,
1322                    &typeSetFlags, &config);
1323            if (kThrowOnBadId) {
1324                if (newBlock == BAD_INDEX) {
1325                    jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1326                    return JNI_FALSE;
1327                }
1328            }
1329            if (newBlock >= 0) block = newBlock;
1330        }
1331
1332        // Deal with the special @null value -- it turns back to TYPE_NULL.
1333        if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
1334            value.dataType = Res_value::TYPE_NULL;
1335            value.data = Res_value::DATA_NULL_UNDEFINED;
1336        }
1337
1338        //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
1339
1340        // Write the final value back to Java.
1341        dest[STYLE_TYPE] = value.dataType;
1342        dest[STYLE_DATA] = value.data;
1343        dest[STYLE_ASSET_COOKIE] = reinterpret_cast<jint>(res.getTableCookie(block));
1344        dest[STYLE_RESOURCE_ID] = resid;
1345        dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
1346        dest[STYLE_DENSITY] = config.density;
1347        dest += STYLE_NUM_ENTRIES;
1348        i+= STYLE_NUM_ENTRIES;
1349        arrayEnt++;
1350    }
1351
1352    i /= STYLE_NUM_ENTRIES;
1353
1354    res.unlock();
1355
1356    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1357
1358    return i;
1359}
1360
1361static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz,
1362                                                         jint cookie,
1363                                                         jstring fileName)
1364{
1365    AssetManager* am = assetManagerForJavaObject(env, clazz);
1366    if (am == NULL) {
1367        return 0;
1368    }
1369
1370    ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz);
1371
1372    ScopedUtfChars fileName8(env, fileName);
1373    if (fileName8.c_str() == NULL) {
1374        return 0;
1375    }
1376
1377    int32_t assetCookie = static_cast<int32_t>(cookie);
1378    Asset* a = assetCookie
1379        ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER)
1380        : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie);
1381
1382    if (a == NULL) {
1383        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
1384        return 0;
1385    }
1386
1387    const DynamicRefTable* dynamicRefTable =
1388            am->getResources().getDynamicRefTableForCookie(assetCookie);
1389    ResXMLTree* block = new ResXMLTree(dynamicRefTable);
1390    status_t err = block->setTo(a->getBuffer(true), a->getLength(), true);
1391    a->close();
1392    delete a;
1393
1394    if (err != NO_ERROR) {
1395        jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
1396        return 0;
1397    }
1398
1399    return reinterpret_cast<jlong>(block);
1400}
1401
1402static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz,
1403                                                                 jint arrayResId)
1404{
1405    AssetManager* am = assetManagerForJavaObject(env, clazz);
1406    if (am == NULL) {
1407        return NULL;
1408    }
1409    const ResTable& res(am->getResources());
1410
1411    const ResTable::bag_entry* startOfBag;
1412    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1413    if (N < 0) {
1414        return NULL;
1415    }
1416
1417    jintArray array = env->NewIntArray(N * 2);
1418    if (array == NULL) {
1419        res.unlockBag(startOfBag);
1420        return NULL;
1421    }
1422
1423    Res_value value;
1424    const ResTable::bag_entry* bag = startOfBag;
1425    for (size_t i = 0, j = 0; ((ssize_t)i)<N; i++, bag++) {
1426        jint stringIndex = -1;
1427        jint stringBlock = 0;
1428        value = bag->map.value;
1429
1430        // Take care of resolving the found resource to its final value.
1431        stringBlock = res.resolveReference(&value, bag->stringBlock, NULL);
1432        if (value.dataType == Res_value::TYPE_STRING) {
1433            stringIndex = value.data;
1434        }
1435
1436        if (kThrowOnBadId) {
1437            if (stringBlock == BAD_INDEX) {
1438                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1439                return array;
1440            }
1441        }
1442
1443        //todo: It might be faster to allocate a C array to contain
1444        //      the blocknums and indices, put them in there and then
1445        //      do just one SetIntArrayRegion()
1446        env->SetIntArrayRegion(array, j, 1, &stringBlock);
1447        env->SetIntArrayRegion(array, j + 1, 1, &stringIndex);
1448        j = j + 2;
1449    }
1450    res.unlockBag(startOfBag);
1451    return array;
1452}
1453
1454static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz,
1455                                                                        jint arrayResId)
1456{
1457    AssetManager* am = assetManagerForJavaObject(env, clazz);
1458    if (am == NULL) {
1459        return NULL;
1460    }
1461    const ResTable& res(am->getResources());
1462
1463    const ResTable::bag_entry* startOfBag;
1464    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1465    if (N < 0) {
1466        return NULL;
1467    }
1468
1469    jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL);
1470    if (env->ExceptionCheck()) {
1471        res.unlockBag(startOfBag);
1472        return NULL;
1473    }
1474
1475    Res_value value;
1476    const ResTable::bag_entry* bag = startOfBag;
1477    size_t strLen = 0;
1478    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
1479        value = bag->map.value;
1480        jstring str = NULL;
1481
1482        // Take care of resolving the found resource to its final value.
1483        ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
1484        if (kThrowOnBadId) {
1485            if (block == BAD_INDEX) {
1486                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1487                return array;
1488            }
1489        }
1490        if (value.dataType == Res_value::TYPE_STRING) {
1491            const ResStringPool* pool = res.getTableStringBlock(block);
1492            const char* str8 = pool->string8At(value.data, &strLen);
1493            if (str8 != NULL) {
1494                str = env->NewStringUTF(str8);
1495            } else {
1496                const char16_t* str16 = pool->stringAt(value.data, &strLen);
1497                str = env->NewString(reinterpret_cast<const jchar*>(str16),
1498                                     strLen);
1499            }
1500
1501            // If one of our NewString{UTF} calls failed due to memory, an
1502            // exception will be pending.
1503            if (env->ExceptionCheck()) {
1504                res.unlockBag(startOfBag);
1505                return NULL;
1506            }
1507
1508            env->SetObjectArrayElement(array, i, str);
1509
1510            // str is not NULL at that point, otherwise ExceptionCheck would have been true.
1511            // If we have a large amount of strings in our array, we might
1512            // overflow the local reference table of the VM.
1513            env->DeleteLocalRef(str);
1514        }
1515    }
1516    res.unlockBag(startOfBag);
1517    return array;
1518}
1519
1520static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz,
1521                                                                        jint arrayResId)
1522{
1523    AssetManager* am = assetManagerForJavaObject(env, clazz);
1524    if (am == NULL) {
1525        return NULL;
1526    }
1527    const ResTable& res(am->getResources());
1528
1529    const ResTable::bag_entry* startOfBag;
1530    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1531    if (N < 0) {
1532        return NULL;
1533    }
1534
1535    jintArray array = env->NewIntArray(N);
1536    if (array == NULL) {
1537        res.unlockBag(startOfBag);
1538        return NULL;
1539    }
1540
1541    Res_value value;
1542    const ResTable::bag_entry* bag = startOfBag;
1543    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
1544        value = bag->map.value;
1545
1546        // Take care of resolving the found resource to its final value.
1547        ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
1548        if (kThrowOnBadId) {
1549            if (block == BAD_INDEX) {
1550                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1551                return array;
1552            }
1553        }
1554        if (value.dataType >= Res_value::TYPE_FIRST_INT
1555                && value.dataType <= Res_value::TYPE_LAST_INT) {
1556            int intVal = value.data;
1557            env->SetIntArrayRegion(array, i, 1, &intVal);
1558        }
1559    }
1560    res.unlockBag(startOfBag);
1561    return array;
1562}
1563
1564static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz,
1565                                                                 jint styleId)
1566{
1567    AssetManager* am = assetManagerForJavaObject(env, clazz);
1568    if (am == NULL) {
1569        return NULL;
1570    }
1571    const ResTable& res(am->getResources());
1572
1573    const ResTable::bag_entry* startOfBag;
1574    const ssize_t N = res.lockBag(styleId, &startOfBag);
1575    if (N < 0) {
1576        return NULL;
1577    }
1578
1579    jintArray array = env->NewIntArray(N);
1580    if (array == NULL) {
1581        res.unlockBag(startOfBag);
1582        return NULL;
1583    }
1584
1585    const ResTable::bag_entry* bag = startOfBag;
1586    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
1587        int resourceId = bag->map.name.ident;
1588        env->SetIntArrayRegion(array, i, 1, &resourceId);
1589    }
1590    res.unlockBag(startOfBag);
1591    return array;
1592}
1593
1594static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
1595{
1596    if (isSystem) {
1597        verifySystemIdmaps();
1598    }
1599    AssetManager* am = new AssetManager();
1600    if (am == NULL) {
1601        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1602        return;
1603    }
1604
1605    am->addDefaultAssets();
1606
1607    ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
1608    env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am));
1609}
1610
1611static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz)
1612{
1613    AssetManager* am = (AssetManager*)
1614        (env->GetLongField(clazz, gAssetManagerOffsets.mObject));
1615    ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz);
1616    if (am != NULL) {
1617        delete am;
1618        env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0);
1619    }
1620}
1621
1622static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz)
1623{
1624    return Asset::getGlobalCount();
1625}
1626
1627static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz)
1628{
1629    String8 alloc = Asset::getAssetAllocations();
1630    if (alloc.length() <= 0) {
1631        return NULL;
1632    }
1633
1634    jstring str = env->NewStringUTF(alloc.string());
1635    return str;
1636}
1637
1638static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz)
1639{
1640    return AssetManager::getGlobalCount();
1641}
1642
1643// ----------------------------------------------------------------------------
1644
1645/*
1646 * JNI registration.
1647 */
1648static const JNINativeMethod gAssetManagerMethods[] = {
1649    /* name, signature, funcPtr */
1650
1651    // Basic asset stuff.
1652    { "openAsset",      "(Ljava/lang/String;I)J",
1653        (void*) android_content_AssetManager_openAsset },
1654    { "openAssetFd",      "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1655        (void*) android_content_AssetManager_openAssetFd },
1656    { "openNonAssetNative", "(ILjava/lang/String;I)J",
1657        (void*) android_content_AssetManager_openNonAssetNative },
1658    { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1659        (void*) android_content_AssetManager_openNonAssetFdNative },
1660    { "list",           "(Ljava/lang/String;)[Ljava/lang/String;",
1661        (void*) android_content_AssetManager_list },
1662    { "destroyAsset",   "(J)V",
1663        (void*) android_content_AssetManager_destroyAsset },
1664    { "readAssetChar",  "(J)I",
1665        (void*) android_content_AssetManager_readAssetChar },
1666    { "readAsset",      "(J[BII)I",
1667        (void*) android_content_AssetManager_readAsset },
1668    { "seekAsset",      "(JJI)J",
1669        (void*) android_content_AssetManager_seekAsset },
1670    { "getAssetLength", "(J)J",
1671        (void*) android_content_AssetManager_getAssetLength },
1672    { "getAssetRemainingLength", "(J)J",
1673        (void*) android_content_AssetManager_getAssetRemainingLength },
1674    { "addAssetPathNative", "(Ljava/lang/String;Z)I",
1675        (void*) android_content_AssetManager_addAssetPath },
1676    { "addOverlayPathNative",   "(Ljava/lang/String;)I",
1677        (void*) android_content_AssetManager_addOverlayPath },
1678    { "isUpToDate",     "()Z",
1679        (void*) android_content_AssetManager_isUpToDate },
1680
1681    // Resources.
1682    { "getLocales",      "()[Ljava/lang/String;",
1683        (void*) android_content_AssetManager_getLocales },
1684    { "getNonSystemLocales", "()[Ljava/lang/String;",
1685        (void*) android_content_AssetManager_getNonSystemLocales },
1686    { "getSizeConfigurations", "()[Landroid/content/res/Configuration;",
1687        (void*) android_content_AssetManager_getSizeConfigurations },
1688    { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V",
1689        (void*) android_content_AssetManager_setConfiguration },
1690    { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
1691        (void*) android_content_AssetManager_getResourceIdentifier },
1692    { "getResourceName","(I)Ljava/lang/String;",
1693        (void*) android_content_AssetManager_getResourceName },
1694    { "getResourcePackageName","(I)Ljava/lang/String;",
1695        (void*) android_content_AssetManager_getResourcePackageName },
1696    { "getResourceTypeName","(I)Ljava/lang/String;",
1697        (void*) android_content_AssetManager_getResourceTypeName },
1698    { "getResourceEntryName","(I)Ljava/lang/String;",
1699        (void*) android_content_AssetManager_getResourceEntryName },
1700    { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I",
1701        (void*) android_content_AssetManager_loadResourceValue },
1702    { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I",
1703        (void*) android_content_AssetManager_loadResourceBagValue },
1704    { "getStringBlockCount","()I",
1705        (void*) android_content_AssetManager_getStringBlockCount },
1706    { "getNativeStringBlock","(I)J",
1707        (void*) android_content_AssetManager_getNativeStringBlock },
1708    { "getCookieName","(I)Ljava/lang/String;",
1709        (void*) android_content_AssetManager_getCookieName },
1710    { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;",
1711        (void*) android_content_AssetManager_getAssignedPackageIdentifiers },
1712
1713    // Themes.
1714    { "newTheme", "()J",
1715        (void*) android_content_AssetManager_newTheme },
1716    { "deleteTheme", "(J)V",
1717        (void*) android_content_AssetManager_deleteTheme },
1718    { "applyThemeStyle", "(JIZ)V",
1719        (void*) android_content_AssetManager_applyThemeStyle },
1720    { "copyTheme", "(JJ)V",
1721        (void*) android_content_AssetManager_copyTheme },
1722    { "clearTheme", "(J)V",
1723        (void*) android_content_AssetManager_clearTheme },
1724    { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
1725        (void*) android_content_AssetManager_loadThemeAttributeValue },
1726    { "getThemeChangingConfigurations", "(J)I",
1727        (void*) android_content_AssetManager_getThemeChangingConfigurations },
1728    { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V",
1729        (void*) android_content_AssetManager_dumpTheme },
1730    { "applyStyle","(JIIJ[IIJJ)V",
1731        (void*) android_content_AssetManager_applyStyle },
1732    { "resolveAttrs","(JII[I[I[I[I)Z",
1733        (void*) android_content_AssetManager_resolveAttrs },
1734    { "retrieveAttributes","(J[I[I[I)Z",
1735        (void*) android_content_AssetManager_retrieveAttributes },
1736    { "getArraySize","(I)I",
1737        (void*) android_content_AssetManager_getArraySize },
1738    { "retrieveArray","(I[I)I",
1739        (void*) android_content_AssetManager_retrieveArray },
1740
1741    // XML files.
1742    { "openXmlAssetNative", "(ILjava/lang/String;)J",
1743        (void*) android_content_AssetManager_openXmlAssetNative },
1744
1745    // Arrays.
1746    { "getArrayStringResource","(I)[Ljava/lang/String;",
1747        (void*) android_content_AssetManager_getArrayStringResource },
1748    { "getArrayStringInfo","(I)[I",
1749        (void*) android_content_AssetManager_getArrayStringInfo },
1750    { "getArrayIntResource","(I)[I",
1751        (void*) android_content_AssetManager_getArrayIntResource },
1752    { "getStyleAttributes","(I)[I",
1753        (void*) android_content_AssetManager_getStyleAttributes },
1754
1755    // Bookkeeping.
1756    { "init",           "(Z)V",
1757        (void*) android_content_AssetManager_init },
1758    { "destroy",        "()V",
1759        (void*) android_content_AssetManager_destroy },
1760    { "getGlobalAssetCount", "()I",
1761        (void*) android_content_AssetManager_getGlobalAssetCount },
1762    { "getAssetAllocations", "()Ljava/lang/String;",
1763        (void*) android_content_AssetManager_getAssetAllocations },
1764    { "getGlobalAssetManagerCount", "()I",
1765        (void*) android_content_AssetManager_getGlobalAssetManagerCount },
1766};
1767
1768int register_android_content_AssetManager(JNIEnv* env)
1769{
1770    jclass typedValue = FindClassOrDie(env, "android/util/TypedValue");
1771    gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I");
1772    gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I");
1773    gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string",
1774                                                 "Ljava/lang/CharSequence;");
1775    gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I");
1776    gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I");
1777    gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue,
1778                                                                 "changingConfigurations", "I");
1779    gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
1780
1781    jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
1782    gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd",
1783                                                      "Landroid/os/ParcelFileDescriptor;");
1784    gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
1785    gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
1786
1787    jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
1788    gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
1789
1790    jclass stringClass = FindClassOrDie(env, "java/lang/String");
1791    g_stringClass = MakeGlobalRefOrDie(env, stringClass);
1792
1793    jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
1794    gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
1795    gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject,
1796                                                       "<init>", "()V");
1797    gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put",
1798                                               "(ILjava/lang/Object;)V");
1799
1800    jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
1801    gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
1802    gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass,
1803            "<init>", "()V");
1804    gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
1805            "smallestScreenWidthDp", "I");
1806    gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
1807            "screenWidthDp", "I");
1808    gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass,
1809            "screenHeightDp", "I");
1810
1811    return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
1812                                NELEM(gAssetManagerMethods));
1813}
1814
1815}; // namespace android
1816