android_util_AssetManager.cpp revision ca3872ce36c94090ae18519dc7fe0cf39d834c4a
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 jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz,
572                                                    jobject fileDescriptor, jstring debugPathName,
573                                                    jboolean appAsLib)
574{
575    ScopedUtfChars debugPathName8(env, debugPathName);
576
577    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
578    if (fd < 0) {
579        jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
580        return 0;
581    }
582
583    AssetManager* am = assetManagerForJavaObject(env, clazz);
584    if (am == NULL) {
585        return 0;
586    }
587
588    int dupfd = ::dup(fd);
589    if (dupfd < 0) {
590        jniThrowIOException(env, errno);
591        return 0;
592    }
593
594    int32_t cookie;
595    bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib);
596
597    return (res) ? static_cast<jint>(cookie) : 0;
598}
599
600static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz)
601{
602    AssetManager* am = assetManagerForJavaObject(env, clazz);
603    if (am == NULL) {
604        return JNI_TRUE;
605    }
606    return am->isUpToDate() ? JNI_TRUE : JNI_FALSE;
607}
608
609static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales)
610{
611    Vector<String8> locales;
612
613    AssetManager* am = assetManagerForJavaObject(env, clazz);
614    if (am == NULL) {
615        return NULL;
616    }
617
618    am->getLocales(&locales, includeSystemLocales);
619
620    const int N = locales.size();
621
622    jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL);
623    if (result == NULL) {
624        return NULL;
625    }
626
627    for (int i=0; i<N; i++) {
628        jstring str = env->NewStringUTF(locales[i].string());
629        if (str == NULL) {
630            return NULL;
631        }
632        env->SetObjectArrayElement(result, i, str);
633        env->DeleteLocalRef(str);
634    }
635
636    return result;
637}
638
639static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
640{
641    return getLocales(env, clazz, true /* include system locales */);
642}
643
644static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz)
645{
646    return getLocales(env, clazz, false /* don't include system locales */);
647}
648
649static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
650    jobject result = env->NewObject(gConfigurationOffsets.classObject,
651            gConfigurationOffsets.constructor);
652    if (result == NULL) {
653        return NULL;
654    }
655
656    env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset,
657            config.smallestScreenWidthDp);
658    env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
659    env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
660
661    return result;
662}
663
664static jobjectArray getSizeConfigurationsInternal(JNIEnv* env,
665        const Vector<ResTable_config>& configs) {
666    const int N = configs.size();
667    jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL);
668    if (result == NULL) {
669        return NULL;
670    }
671
672    for (int i=0; i<N; i++) {
673        jobject config = constructConfigurationObject(env, configs[i]);
674        if (config == NULL) {
675            env->DeleteLocalRef(result);
676            return NULL;
677        }
678
679        env->SetObjectArrayElement(result, i, config);
680        env->DeleteLocalRef(config);
681    }
682
683    return result;
684}
685
686static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) {
687    AssetManager* am = assetManagerForJavaObject(env, clazz);
688    if (am == NULL) {
689        return NULL;
690    }
691
692    const ResTable& res(am->getResources());
693    Vector<ResTable_config> configs;
694    res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */);
695
696    return getSizeConfigurationsInternal(env, configs);
697}
698
699static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz,
700                                                          jint mcc, jint mnc,
701                                                          jstring locale, jint orientation,
702                                                          jint touchscreen, jint density,
703                                                          jint keyboard, jint keyboardHidden,
704                                                          jint navigation,
705                                                          jint screenWidth, jint screenHeight,
706                                                          jint smallestScreenWidthDp,
707                                                          jint screenWidthDp, jint screenHeightDp,
708                                                          jint screenLayout, jint uiMode,
709                                                          jint colorMode, jint sdkVersion)
710{
711    AssetManager* am = assetManagerForJavaObject(env, clazz);
712    if (am == NULL) {
713        return;
714    }
715
716    ResTable_config config;
717    memset(&config, 0, sizeof(config));
718
719    const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL;
720
721    // Constants duplicated from Java class android.content.res.Configuration.
722    static const jint kScreenLayoutRoundMask = 0x300;
723    static const jint kScreenLayoutRoundShift = 8;
724
725    config.mcc = (uint16_t)mcc;
726    config.mnc = (uint16_t)mnc;
727    config.orientation = (uint8_t)orientation;
728    config.touchscreen = (uint8_t)touchscreen;
729    config.density = (uint16_t)density;
730    config.keyboard = (uint8_t)keyboard;
731    config.inputFlags = (uint8_t)keyboardHidden;
732    config.navigation = (uint8_t)navigation;
733    config.screenWidth = (uint16_t)screenWidth;
734    config.screenHeight = (uint16_t)screenHeight;
735    config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp;
736    config.screenWidthDp = (uint16_t)screenWidthDp;
737    config.screenHeightDp = (uint16_t)screenHeightDp;
738    config.screenLayout = (uint8_t)screenLayout;
739    config.uiMode = (uint8_t)uiMode;
740    config.colorMode = (uint8_t)colorMode;
741    config.sdkVersion = (uint16_t)sdkVersion;
742    config.minorVersion = 0;
743
744    // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
745    // in C++. We must extract the round qualifier out of the Java screenLayout and put it
746    // into screenLayout2.
747    config.screenLayout2 =
748            (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
749
750    am->setConfiguration(config, locale8);
751
752    if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8);
753}
754
755static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz,
756                                                            jstring name,
757                                                            jstring defType,
758                                                            jstring defPackage)
759{
760    ScopedStringChars name16(env, name);
761    if (name16.get() == NULL) {
762        return 0;
763    }
764
765    AssetManager* am = assetManagerForJavaObject(env, clazz);
766    if (am == NULL) {
767        return 0;
768    }
769
770    const char16_t* defType16 = reinterpret_cast<const char16_t*>(defType)
771        ? reinterpret_cast<const char16_t*>(env->GetStringChars(defType, NULL))
772        : NULL;
773    jsize defTypeLen = defType
774        ? env->GetStringLength(defType) : 0;
775    const char16_t* defPackage16 = reinterpret_cast<const char16_t*>(defPackage)
776        ? reinterpret_cast<const char16_t*>(env->GetStringChars(defPackage,
777                                                                NULL))
778        : NULL;
779    jsize defPackageLen = defPackage
780        ? env->GetStringLength(defPackage) : 0;
781
782    jint ident = am->getResources().identifierForName(
783        reinterpret_cast<const char16_t*>(name16.get()), name16.size(),
784        defType16, defTypeLen, defPackage16, defPackageLen);
785
786    if (defPackage16) {
787        env->ReleaseStringChars(defPackage,
788                                reinterpret_cast<const jchar*>(defPackage16));
789    }
790    if (defType16) {
791        env->ReleaseStringChars(defType,
792                                reinterpret_cast<const jchar*>(defType16));
793    }
794
795    return ident;
796}
797
798static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz,
799                                                            jint resid)
800{
801    AssetManager* am = assetManagerForJavaObject(env, clazz);
802    if (am == NULL) {
803        return NULL;
804    }
805
806    ResTable::resource_name name;
807    if (!am->getResources().getResourceName(resid, true, &name)) {
808        return NULL;
809    }
810
811    String16 str;
812    if (name.package != NULL) {
813        str.setTo(name.package, name.packageLen);
814    }
815    if (name.type8 != NULL || name.type != NULL) {
816        if (str.size() > 0) {
817            char16_t div = ':';
818            str.append(&div, 1);
819        }
820        if (name.type8 != NULL) {
821            str.append(String16(name.type8, name.typeLen));
822        } else {
823            str.append(name.type, name.typeLen);
824        }
825    }
826    if (name.name8 != NULL || name.name != NULL) {
827        if (str.size() > 0) {
828            char16_t div = '/';
829            str.append(&div, 1);
830        }
831        if (name.name8 != NULL) {
832            str.append(String16(name.name8, name.nameLen));
833        } else {
834            str.append(name.name, name.nameLen);
835        }
836    }
837
838    return env->NewString((const jchar*)str.string(), str.size());
839}
840
841static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz,
842                                                                   jint resid)
843{
844    AssetManager* am = assetManagerForJavaObject(env, clazz);
845    if (am == NULL) {
846        return NULL;
847    }
848
849    ResTable::resource_name name;
850    if (!am->getResources().getResourceName(resid, true, &name)) {
851        return NULL;
852    }
853
854    if (name.package != NULL) {
855        return env->NewString((const jchar*)name.package, name.packageLen);
856    }
857
858    return NULL;
859}
860
861static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz,
862                                                                jint resid)
863{
864    AssetManager* am = assetManagerForJavaObject(env, clazz);
865    if (am == NULL) {
866        return NULL;
867    }
868
869    ResTable::resource_name name;
870    if (!am->getResources().getResourceName(resid, true, &name)) {
871        return NULL;
872    }
873
874    if (name.type8 != NULL) {
875        return env->NewStringUTF(name.type8);
876    }
877
878    if (name.type != NULL) {
879        return env->NewString((const jchar*)name.type, name.typeLen);
880    }
881
882    return NULL;
883}
884
885static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz,
886                                                                 jint resid)
887{
888    AssetManager* am = assetManagerForJavaObject(env, clazz);
889    if (am == NULL) {
890        return NULL;
891    }
892
893    ResTable::resource_name name;
894    if (!am->getResources().getResourceName(resid, true, &name)) {
895        return NULL;
896    }
897
898    if (name.name8 != NULL) {
899        return env->NewStringUTF(name.name8);
900    }
901
902    if (name.name != NULL) {
903        return env->NewString((const jchar*)name.name, name.nameLen);
904    }
905
906    return NULL;
907}
908
909static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz,
910                                                           jint ident,
911                                                           jshort density,
912                                                           jobject outValue,
913                                                           jboolean resolve)
914{
915    if (outValue == NULL) {
916         jniThrowNullPointerException(env, "outValue");
917         return 0;
918    }
919    AssetManager* am = assetManagerForJavaObject(env, clazz);
920    if (am == NULL) {
921        return 0;
922    }
923    const ResTable& res(am->getResources());
924
925    Res_value value;
926    ResTable_config config;
927    uint32_t typeSpecFlags;
928    ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config);
929    if (kThrowOnBadId) {
930        if (block == BAD_INDEX) {
931            jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
932            return 0;
933        }
934    }
935    uint32_t ref = ident;
936    if (resolve) {
937        block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config);
938        if (kThrowOnBadId) {
939            if (block == BAD_INDEX) {
940                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
941                return 0;
942            }
943        }
944    }
945    if (block >= 0) {
946        return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config);
947    }
948
949    return static_cast<jint>(block);
950}
951
952static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz,
953                                                           jint ident, jint bagEntryId,
954                                                           jobject outValue, jboolean resolve)
955{
956    AssetManager* am = assetManagerForJavaObject(env, clazz);
957    if (am == NULL) {
958        return 0;
959    }
960    const ResTable& res(am->getResources());
961
962    // Now lock down the resource object and start pulling stuff from it.
963    res.lock();
964
965    ssize_t block = -1;
966    Res_value value;
967
968    const ResTable::bag_entry* entry = NULL;
969    uint32_t typeSpecFlags;
970    ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags);
971
972    for (ssize_t i=0; i<entryCount; i++) {
973        if (((uint32_t)bagEntryId) == entry->map.name.ident) {
974            block = entry->stringBlock;
975            value = entry->map.value;
976        }
977        entry++;
978    }
979
980    res.unlock();
981
982    if (block < 0) {
983        return static_cast<jint>(block);
984    }
985
986    uint32_t ref = ident;
987    if (resolve) {
988        block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
989        if (kThrowOnBadId) {
990            if (block == BAD_INDEX) {
991                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
992                return 0;
993            }
994        }
995    }
996    if (block >= 0) {
997        return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags);
998    }
999
1000    return static_cast<jint>(block);
1001}
1002
1003static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz)
1004{
1005    AssetManager* am = assetManagerForJavaObject(env, clazz);
1006    if (am == NULL) {
1007        return 0;
1008    }
1009    return am->getResources().getTableCount();
1010}
1011
1012static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz,
1013                                                           jint block)
1014{
1015    AssetManager* am = assetManagerForJavaObject(env, clazz);
1016    if (am == NULL) {
1017        return 0;
1018    }
1019    return reinterpret_cast<jlong>(am->getResources().getTableStringBlock(block));
1020}
1021
1022static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz,
1023                                                       jint cookie)
1024{
1025    AssetManager* am = assetManagerForJavaObject(env, clazz);
1026    if (am == NULL) {
1027        return NULL;
1028    }
1029    String8 name(am->getAssetPath(static_cast<int32_t>(cookie)));
1030    if (name.length() == 0) {
1031        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name");
1032        return NULL;
1033    }
1034    jstring str = env->NewStringUTF(name.string());
1035    return str;
1036}
1037
1038static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz)
1039{
1040    AssetManager* am = assetManagerForJavaObject(env, clazz);
1041    if (am == NULL) {
1042        return 0;
1043    }
1044
1045    const ResTable& res = am->getResources();
1046
1047    jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject,
1048            gSparseArrayOffsets.constructor);
1049    const size_t N = res.getBasePackageCount();
1050    for (size_t i = 0; i < N; i++) {
1051        const String16 name = res.getBasePackageName(i);
1052        env->CallVoidMethod(
1053            sparseArray, gSparseArrayOffsets.put,
1054            static_cast<jint>(res.getBasePackageId(i)),
1055            env->NewString(reinterpret_cast<const jchar*>(name.string()),
1056                           name.size()));
1057    }
1058    return sparseArray;
1059}
1060
1061static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz)
1062{
1063    AssetManager* am = assetManagerForJavaObject(env, clazz);
1064    if (am == NULL) {
1065        return 0;
1066    }
1067    return reinterpret_cast<jlong>(new ResTable::Theme(am->getResources()));
1068}
1069
1070static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz,
1071                                                     jlong themeHandle)
1072{
1073    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
1074    delete theme;
1075}
1076
1077static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz,
1078                                                         jlong themeHandle,
1079                                                         jint styleRes,
1080                                                         jboolean force)
1081{
1082    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
1083    theme->applyStyle(styleRes, force ? true : false);
1084}
1085
1086static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz,
1087                                                   jlong destHandle, jlong srcHandle)
1088{
1089    ResTable::Theme* dest = reinterpret_cast<ResTable::Theme*>(destHandle);
1090    ResTable::Theme* src = reinterpret_cast<ResTable::Theme*>(srcHandle);
1091    dest->setTo(*src);
1092}
1093
1094static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle)
1095{
1096    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
1097    theme->clear();
1098}
1099
1100static jint android_content_AssetManager_loadThemeAttributeValue(
1101    JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve)
1102{
1103    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
1104    const ResTable& res(theme->getResTable());
1105
1106    Res_value value;
1107    // XXX value could be different in different configs!
1108    uint32_t typeSpecFlags = 0;
1109    ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags);
1110    uint32_t ref = 0;
1111    if (resolve) {
1112        block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
1113        if (kThrowOnBadId) {
1114            if (block == BAD_INDEX) {
1115                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1116                return 0;
1117            }
1118        }
1119    }
1120    return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block;
1121}
1122
1123static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz,
1124                                                                        jlong themeHandle)
1125{
1126    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
1127    return theme->getChangingConfigurations();
1128}
1129
1130static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz,
1131                                                   jlong themeHandle, jint pri,
1132                                                   jstring tag, jstring prefix)
1133{
1134    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
1135    const ResTable& res(theme->getResTable());
1136    (void)res;
1137
1138    // XXX Need to use params.
1139    theme->dumpToLog();
1140}
1141
1142static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz,
1143                                                          jlong themeToken,
1144                                                          jint defStyleAttr,
1145                                                          jint defStyleRes,
1146                                                          jintArray inValues,
1147                                                          jintArray attrs,
1148                                                          jintArray outValues,
1149                                                          jintArray outIndices)
1150{
1151    if (themeToken == 0) {
1152        jniThrowNullPointerException(env, "theme token");
1153        return JNI_FALSE;
1154    }
1155    if (attrs == NULL) {
1156        jniThrowNullPointerException(env, "attrs");
1157        return JNI_FALSE;
1158    }
1159    if (outValues == NULL) {
1160        jniThrowNullPointerException(env, "out values");
1161        return JNI_FALSE;
1162    }
1163
1164    const jsize NI = env->GetArrayLength(attrs);
1165    const jsize NV = env->GetArrayLength(outValues);
1166    if (NV < (NI*STYLE_NUM_ENTRIES)) {
1167        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
1168        return JNI_FALSE;
1169    }
1170
1171    jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
1172    if (src == NULL) {
1173        return JNI_FALSE;
1174    }
1175
1176    jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0);
1177    const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues);
1178
1179    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1180    if (baseDest == NULL) {
1181        env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1182        return JNI_FALSE;
1183    }
1184
1185    jint* indices = NULL;
1186    if (outIndices != NULL) {
1187        if (env->GetArrayLength(outIndices) > NI) {
1188            indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
1189        }
1190    }
1191
1192    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
1193    bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes,
1194                               (uint32_t*) srcValues, NSV,
1195                               (uint32_t*) src, NI,
1196                               (uint32_t*) baseDest,
1197                               (uint32_t*) indices);
1198
1199    if (indices != NULL) {
1200        env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
1201    }
1202    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1203    env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0);
1204    env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1205    return result ? JNI_TRUE : JNI_FALSE;
1206}
1207
1208static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken,
1209        jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length,
1210        jlong outValuesAddress, jlong outIndicesAddress) {
1211    jint* attrs = env->GetIntArrayElements(attrsObj, 0);
1212    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
1213    ResXMLParser* xmlParser = reinterpret_cast<ResXMLParser*>(xmlParserToken);
1214    uint32_t* outValues = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outValuesAddress));
1215    uint32_t* outIndices = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outIndicesAddress));
1216    ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes,
1217            reinterpret_cast<const uint32_t*>(attrs), length, outValues, outIndices);
1218    env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT);
1219}
1220
1221static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz,
1222                                                        jlong xmlParserToken,
1223                                                        jintArray attrs,
1224                                                        jintArray outValues,
1225                                                        jintArray outIndices)
1226{
1227    if (xmlParserToken == 0) {
1228        jniThrowNullPointerException(env, "xmlParserToken");
1229        return JNI_FALSE;
1230    }
1231    if (attrs == NULL) {
1232        jniThrowNullPointerException(env, "attrs");
1233        return JNI_FALSE;
1234    }
1235    if (outValues == NULL) {
1236        jniThrowNullPointerException(env, "out values");
1237        return JNI_FALSE;
1238    }
1239
1240    AssetManager* am = assetManagerForJavaObject(env, clazz);
1241    if (am == NULL) {
1242        return JNI_FALSE;
1243    }
1244    const ResTable& res(am->getResources());
1245    ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
1246
1247    const jsize NI = env->GetArrayLength(attrs);
1248    const jsize NV = env->GetArrayLength(outValues);
1249    if (NV < (NI*STYLE_NUM_ENTRIES)) {
1250        jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
1251        return JNI_FALSE;
1252    }
1253
1254    jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
1255    if (src == NULL) {
1256        return JNI_FALSE;
1257    }
1258
1259    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1260    if (baseDest == NULL) {
1261        env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1262        return JNI_FALSE;
1263    }
1264
1265    jint* indices = NULL;
1266    if (outIndices != NULL) {
1267        if (env->GetArrayLength(outIndices) > NI) {
1268            indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
1269        }
1270    }
1271
1272    bool result = RetrieveAttributes(&res, xmlParser,
1273                                     (uint32_t*) src, NI,
1274                                     (uint32_t*) baseDest,
1275                                     (uint32_t*) indices);
1276
1277    if (indices != NULL) {
1278        env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
1279    }
1280    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1281    env->ReleasePrimitiveArrayCritical(attrs, src, 0);
1282    return result ? JNI_TRUE : JNI_FALSE;
1283}
1284
1285static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz,
1286                                                       jint id)
1287{
1288    AssetManager* am = assetManagerForJavaObject(env, clazz);
1289    if (am == NULL) {
1290        return 0;
1291    }
1292    const ResTable& res(am->getResources());
1293
1294    res.lock();
1295    const ResTable::bag_entry* defStyleEnt = NULL;
1296    ssize_t bagOff = res.getBagLocked(id, &defStyleEnt);
1297    res.unlock();
1298
1299    return static_cast<jint>(bagOff);
1300}
1301
1302static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz,
1303                                                        jint id,
1304                                                        jintArray outValues)
1305{
1306    if (outValues == NULL) {
1307        jniThrowNullPointerException(env, "out values");
1308        return JNI_FALSE;
1309    }
1310
1311    AssetManager* am = assetManagerForJavaObject(env, clazz);
1312    if (am == NULL) {
1313        return JNI_FALSE;
1314    }
1315    const ResTable& res(am->getResources());
1316    ResTable_config config;
1317    Res_value value;
1318    ssize_t block;
1319
1320    const jsize NV = env->GetArrayLength(outValues);
1321
1322    jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
1323    jint* dest = baseDest;
1324    if (dest == NULL) {
1325        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1326        return JNI_FALSE;
1327    }
1328
1329    // Now lock down the resource object and start pulling stuff from it.
1330    res.lock();
1331
1332    const ResTable::bag_entry* arrayEnt = NULL;
1333    uint32_t arrayTypeSetFlags = 0;
1334    ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags);
1335    const ResTable::bag_entry* endArrayEnt = arrayEnt +
1336        (bagOff >= 0 ? bagOff : 0);
1337
1338    int i = 0;
1339    uint32_t typeSetFlags;
1340    while (i < NV && arrayEnt < endArrayEnt) {
1341        block = arrayEnt->stringBlock;
1342        typeSetFlags = arrayTypeSetFlags;
1343        config.density = 0;
1344        value = arrayEnt->map.value;
1345
1346        uint32_t resid = 0;
1347        if (value.dataType != Res_value::TYPE_NULL) {
1348            // Take care of resolving the found resource to its final value.
1349            //printf("Resolving attribute reference\n");
1350            ssize_t newBlock = res.resolveReference(&value, block, &resid,
1351                    &typeSetFlags, &config);
1352            if (kThrowOnBadId) {
1353                if (newBlock == BAD_INDEX) {
1354                    jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1355                    return JNI_FALSE;
1356                }
1357            }
1358            if (newBlock >= 0) block = newBlock;
1359        }
1360
1361        // Deal with the special @null value -- it turns back to TYPE_NULL.
1362        if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
1363            value.dataType = Res_value::TYPE_NULL;
1364            value.data = Res_value::DATA_NULL_UNDEFINED;
1365        }
1366
1367        //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
1368
1369        // Write the final value back to Java.
1370        dest[STYLE_TYPE] = value.dataType;
1371        dest[STYLE_DATA] = value.data;
1372        dest[STYLE_ASSET_COOKIE] = reinterpret_cast<jint>(res.getTableCookie(block));
1373        dest[STYLE_RESOURCE_ID] = resid;
1374        dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
1375        dest[STYLE_DENSITY] = config.density;
1376        dest += STYLE_NUM_ENTRIES;
1377        i+= STYLE_NUM_ENTRIES;
1378        arrayEnt++;
1379    }
1380
1381    i /= STYLE_NUM_ENTRIES;
1382
1383    res.unlock();
1384
1385    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
1386
1387    return i;
1388}
1389
1390static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz,
1391                                                         jint cookie,
1392                                                         jstring fileName)
1393{
1394    AssetManager* am = assetManagerForJavaObject(env, clazz);
1395    if (am == NULL) {
1396        return 0;
1397    }
1398
1399    ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz);
1400
1401    ScopedUtfChars fileName8(env, fileName);
1402    if (fileName8.c_str() == NULL) {
1403        return 0;
1404    }
1405
1406    int32_t assetCookie = static_cast<int32_t>(cookie);
1407    Asset* a = assetCookie
1408        ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER)
1409        : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie);
1410
1411    if (a == NULL) {
1412        jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
1413        return 0;
1414    }
1415
1416    const DynamicRefTable* dynamicRefTable =
1417            am->getResources().getDynamicRefTableForCookie(assetCookie);
1418    ResXMLTree* block = new ResXMLTree(dynamicRefTable);
1419    status_t err = block->setTo(a->getBuffer(true), a->getLength(), true);
1420    a->close();
1421    delete a;
1422
1423    if (err != NO_ERROR) {
1424        jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
1425        return 0;
1426    }
1427
1428    return reinterpret_cast<jlong>(block);
1429}
1430
1431static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz,
1432                                                                 jint arrayResId)
1433{
1434    AssetManager* am = assetManagerForJavaObject(env, clazz);
1435    if (am == NULL) {
1436        return NULL;
1437    }
1438    const ResTable& res(am->getResources());
1439
1440    const ResTable::bag_entry* startOfBag;
1441    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1442    if (N < 0) {
1443        return NULL;
1444    }
1445
1446    jintArray array = env->NewIntArray(N * 2);
1447    if (array == NULL) {
1448        res.unlockBag(startOfBag);
1449        return NULL;
1450    }
1451
1452    Res_value value;
1453    const ResTable::bag_entry* bag = startOfBag;
1454    for (size_t i = 0, j = 0; ((ssize_t)i)<N; i++, bag++) {
1455        jint stringIndex = -1;
1456        jint stringBlock = 0;
1457        value = bag->map.value;
1458
1459        // Take care of resolving the found resource to its final value.
1460        stringBlock = res.resolveReference(&value, bag->stringBlock, NULL);
1461        if (value.dataType == Res_value::TYPE_STRING) {
1462            stringIndex = value.data;
1463        }
1464
1465        if (kThrowOnBadId) {
1466            if (stringBlock == BAD_INDEX) {
1467                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1468                return array;
1469            }
1470        }
1471
1472        //todo: It might be faster to allocate a C array to contain
1473        //      the blocknums and indices, put them in there and then
1474        //      do just one SetIntArrayRegion()
1475        env->SetIntArrayRegion(array, j, 1, &stringBlock);
1476        env->SetIntArrayRegion(array, j + 1, 1, &stringIndex);
1477        j = j + 2;
1478    }
1479    res.unlockBag(startOfBag);
1480    return array;
1481}
1482
1483static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz,
1484                                                                        jint arrayResId)
1485{
1486    AssetManager* am = assetManagerForJavaObject(env, clazz);
1487    if (am == NULL) {
1488        return NULL;
1489    }
1490    const ResTable& res(am->getResources());
1491
1492    const ResTable::bag_entry* startOfBag;
1493    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1494    if (N < 0) {
1495        return NULL;
1496    }
1497
1498    jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL);
1499    if (env->ExceptionCheck()) {
1500        res.unlockBag(startOfBag);
1501        return NULL;
1502    }
1503
1504    Res_value value;
1505    const ResTable::bag_entry* bag = startOfBag;
1506    size_t strLen = 0;
1507    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
1508        value = bag->map.value;
1509        jstring str = NULL;
1510
1511        // Take care of resolving the found resource to its final value.
1512        ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
1513        if (kThrowOnBadId) {
1514            if (block == BAD_INDEX) {
1515                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1516                return array;
1517            }
1518        }
1519        if (value.dataType == Res_value::TYPE_STRING) {
1520            const ResStringPool* pool = res.getTableStringBlock(block);
1521            const char* str8 = pool->string8At(value.data, &strLen);
1522            if (str8 != NULL) {
1523                str = env->NewStringUTF(str8);
1524            } else {
1525                const char16_t* str16 = pool->stringAt(value.data, &strLen);
1526                str = env->NewString(reinterpret_cast<const jchar*>(str16),
1527                                     strLen);
1528            }
1529
1530            // If one of our NewString{UTF} calls failed due to memory, an
1531            // exception will be pending.
1532            if (env->ExceptionCheck()) {
1533                res.unlockBag(startOfBag);
1534                return NULL;
1535            }
1536
1537            env->SetObjectArrayElement(array, i, str);
1538
1539            // str is not NULL at that point, otherwise ExceptionCheck would have been true.
1540            // If we have a large amount of strings in our array, we might
1541            // overflow the local reference table of the VM.
1542            env->DeleteLocalRef(str);
1543        }
1544    }
1545    res.unlockBag(startOfBag);
1546    return array;
1547}
1548
1549static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz,
1550                                                                        jint arrayResId)
1551{
1552    AssetManager* am = assetManagerForJavaObject(env, clazz);
1553    if (am == NULL) {
1554        return NULL;
1555    }
1556    const ResTable& res(am->getResources());
1557
1558    const ResTable::bag_entry* startOfBag;
1559    const ssize_t N = res.lockBag(arrayResId, &startOfBag);
1560    if (N < 0) {
1561        return NULL;
1562    }
1563
1564    jintArray array = env->NewIntArray(N);
1565    if (array == NULL) {
1566        res.unlockBag(startOfBag);
1567        return NULL;
1568    }
1569
1570    Res_value value;
1571    const ResTable::bag_entry* bag = startOfBag;
1572    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
1573        value = bag->map.value;
1574
1575        // Take care of resolving the found resource to its final value.
1576        ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
1577        if (kThrowOnBadId) {
1578            if (block == BAD_INDEX) {
1579                jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
1580                return array;
1581            }
1582        }
1583        if (value.dataType >= Res_value::TYPE_FIRST_INT
1584                && value.dataType <= Res_value::TYPE_LAST_INT) {
1585            int intVal = value.data;
1586            env->SetIntArrayRegion(array, i, 1, &intVal);
1587        }
1588    }
1589    res.unlockBag(startOfBag);
1590    return array;
1591}
1592
1593static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz,
1594                                                                 jint styleId)
1595{
1596    AssetManager* am = assetManagerForJavaObject(env, clazz);
1597    if (am == NULL) {
1598        return NULL;
1599    }
1600    const ResTable& res(am->getResources());
1601
1602    const ResTable::bag_entry* startOfBag;
1603    const ssize_t N = res.lockBag(styleId, &startOfBag);
1604    if (N < 0) {
1605        return NULL;
1606    }
1607
1608    jintArray array = env->NewIntArray(N);
1609    if (array == NULL) {
1610        res.unlockBag(startOfBag);
1611        return NULL;
1612    }
1613
1614    const ResTable::bag_entry* bag = startOfBag;
1615    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
1616        int resourceId = bag->map.name.ident;
1617        env->SetIntArrayRegion(array, i, 1, &resourceId);
1618    }
1619    res.unlockBag(startOfBag);
1620    return array;
1621}
1622
1623static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
1624{
1625    if (isSystem) {
1626        verifySystemIdmaps();
1627    }
1628    AssetManager* am = new AssetManager();
1629    if (am == NULL) {
1630        jniThrowException(env, "java/lang/OutOfMemoryError", "");
1631        return;
1632    }
1633
1634    am->addDefaultAssets();
1635
1636    ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
1637    env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am));
1638}
1639
1640static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz)
1641{
1642    AssetManager* am = (AssetManager*)
1643        (env->GetLongField(clazz, gAssetManagerOffsets.mObject));
1644    ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz);
1645    if (am != NULL) {
1646        delete am;
1647        env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0);
1648    }
1649}
1650
1651static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz)
1652{
1653    return Asset::getGlobalCount();
1654}
1655
1656static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz)
1657{
1658    String8 alloc = Asset::getAssetAllocations();
1659    if (alloc.length() <= 0) {
1660        return NULL;
1661    }
1662
1663    jstring str = env->NewStringUTF(alloc.string());
1664    return str;
1665}
1666
1667static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz)
1668{
1669    return AssetManager::getGlobalCount();
1670}
1671
1672// ----------------------------------------------------------------------------
1673
1674/*
1675 * JNI registration.
1676 */
1677static const JNINativeMethod gAssetManagerMethods[] = {
1678    /* name, signature, funcPtr */
1679
1680    // Basic asset stuff.
1681    { "openAsset",      "(Ljava/lang/String;I)J",
1682        (void*) android_content_AssetManager_openAsset },
1683    { "openAssetFd",      "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1684        (void*) android_content_AssetManager_openAssetFd },
1685    { "openNonAssetNative", "(ILjava/lang/String;I)J",
1686        (void*) android_content_AssetManager_openNonAssetNative },
1687    { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1688        (void*) android_content_AssetManager_openNonAssetFdNative },
1689    { "list",           "(Ljava/lang/String;)[Ljava/lang/String;",
1690        (void*) android_content_AssetManager_list },
1691    { "destroyAsset",   "(J)V",
1692        (void*) android_content_AssetManager_destroyAsset },
1693    { "readAssetChar",  "(J)I",
1694        (void*) android_content_AssetManager_readAssetChar },
1695    { "readAsset",      "(J[BII)I",
1696        (void*) android_content_AssetManager_readAsset },
1697    { "seekAsset",      "(JJI)J",
1698        (void*) android_content_AssetManager_seekAsset },
1699    { "getAssetLength", "(J)J",
1700        (void*) android_content_AssetManager_getAssetLength },
1701    { "getAssetRemainingLength", "(J)J",
1702        (void*) android_content_AssetManager_getAssetRemainingLength },
1703    { "addAssetPathNative", "(Ljava/lang/String;Z)I",
1704        (void*) android_content_AssetManager_addAssetPath },
1705    { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I",
1706        (void*) android_content_AssetManager_addAssetFd },
1707    { "addOverlayPathNative",   "(Ljava/lang/String;)I",
1708        (void*) android_content_AssetManager_addOverlayPath },
1709    { "isUpToDate",     "()Z",
1710        (void*) android_content_AssetManager_isUpToDate },
1711
1712    // Resources.
1713    { "getLocales",      "()[Ljava/lang/String;",
1714        (void*) android_content_AssetManager_getLocales },
1715    { "getNonSystemLocales", "()[Ljava/lang/String;",
1716        (void*) android_content_AssetManager_getNonSystemLocales },
1717    { "getSizeConfigurations", "()[Landroid/content/res/Configuration;",
1718        (void*) android_content_AssetManager_getSizeConfigurations },
1719    { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V",
1720        (void*) android_content_AssetManager_setConfiguration },
1721    { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
1722        (void*) android_content_AssetManager_getResourceIdentifier },
1723    { "getResourceName","(I)Ljava/lang/String;",
1724        (void*) android_content_AssetManager_getResourceName },
1725    { "getResourcePackageName","(I)Ljava/lang/String;",
1726        (void*) android_content_AssetManager_getResourcePackageName },
1727    { "getResourceTypeName","(I)Ljava/lang/String;",
1728        (void*) android_content_AssetManager_getResourceTypeName },
1729    { "getResourceEntryName","(I)Ljava/lang/String;",
1730        (void*) android_content_AssetManager_getResourceEntryName },
1731    { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I",
1732        (void*) android_content_AssetManager_loadResourceValue },
1733    { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I",
1734        (void*) android_content_AssetManager_loadResourceBagValue },
1735    { "getStringBlockCount","()I",
1736        (void*) android_content_AssetManager_getStringBlockCount },
1737    { "getNativeStringBlock","(I)J",
1738        (void*) android_content_AssetManager_getNativeStringBlock },
1739    { "getCookieName","(I)Ljava/lang/String;",
1740        (void*) android_content_AssetManager_getCookieName },
1741    { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;",
1742        (void*) android_content_AssetManager_getAssignedPackageIdentifiers },
1743
1744    // Themes.
1745    { "newTheme", "()J",
1746        (void*) android_content_AssetManager_newTheme },
1747    { "deleteTheme", "(J)V",
1748        (void*) android_content_AssetManager_deleteTheme },
1749    { "applyThemeStyle", "(JIZ)V",
1750        (void*) android_content_AssetManager_applyThemeStyle },
1751    { "copyTheme", "(JJ)V",
1752        (void*) android_content_AssetManager_copyTheme },
1753    { "clearTheme", "(J)V",
1754        (void*) android_content_AssetManager_clearTheme },
1755    { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
1756        (void*) android_content_AssetManager_loadThemeAttributeValue },
1757    { "getThemeChangingConfigurations", "(J)I",
1758        (void*) android_content_AssetManager_getThemeChangingConfigurations },
1759    { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V",
1760        (void*) android_content_AssetManager_dumpTheme },
1761    { "applyStyle","(JIIJ[IIJJ)V",
1762        (void*) android_content_AssetManager_applyStyle },
1763    { "resolveAttrs","(JII[I[I[I[I)Z",
1764        (void*) android_content_AssetManager_resolveAttrs },
1765    { "retrieveAttributes","(J[I[I[I)Z",
1766        (void*) android_content_AssetManager_retrieveAttributes },
1767    { "getArraySize","(I)I",
1768        (void*) android_content_AssetManager_getArraySize },
1769    { "retrieveArray","(I[I)I",
1770        (void*) android_content_AssetManager_retrieveArray },
1771
1772    // XML files.
1773    { "openXmlAssetNative", "(ILjava/lang/String;)J",
1774        (void*) android_content_AssetManager_openXmlAssetNative },
1775
1776    // Arrays.
1777    { "getArrayStringResource","(I)[Ljava/lang/String;",
1778        (void*) android_content_AssetManager_getArrayStringResource },
1779    { "getArrayStringInfo","(I)[I",
1780        (void*) android_content_AssetManager_getArrayStringInfo },
1781    { "getArrayIntResource","(I)[I",
1782        (void*) android_content_AssetManager_getArrayIntResource },
1783    { "getStyleAttributes","(I)[I",
1784        (void*) android_content_AssetManager_getStyleAttributes },
1785
1786    // Bookkeeping.
1787    { "init",           "(Z)V",
1788        (void*) android_content_AssetManager_init },
1789    { "destroy",        "()V",
1790        (void*) android_content_AssetManager_destroy },
1791    { "getGlobalAssetCount", "()I",
1792        (void*) android_content_AssetManager_getGlobalAssetCount },
1793    { "getAssetAllocations", "()Ljava/lang/String;",
1794        (void*) android_content_AssetManager_getAssetAllocations },
1795    { "getGlobalAssetManagerCount", "()I",
1796        (void*) android_content_AssetManager_getGlobalAssetManagerCount },
1797};
1798
1799int register_android_content_AssetManager(JNIEnv* env)
1800{
1801    jclass typedValue = FindClassOrDie(env, "android/util/TypedValue");
1802    gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I");
1803    gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I");
1804    gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string",
1805                                                 "Ljava/lang/CharSequence;");
1806    gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I");
1807    gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I");
1808    gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue,
1809                                                                 "changingConfigurations", "I");
1810    gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
1811
1812    jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
1813    gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd",
1814                                                      "Landroid/os/ParcelFileDescriptor;");
1815    gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
1816    gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
1817
1818    jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
1819    gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
1820
1821    jclass stringClass = FindClassOrDie(env, "java/lang/String");
1822    g_stringClass = MakeGlobalRefOrDie(env, stringClass);
1823
1824    jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
1825    gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
1826    gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject,
1827                                                       "<init>", "()V");
1828    gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put",
1829                                               "(ILjava/lang/Object;)V");
1830
1831    jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
1832    gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
1833    gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass,
1834            "<init>", "()V");
1835    gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
1836            "smallestScreenWidthDp", "I");
1837    gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
1838            "screenWidthDp", "I");
1839    gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass,
1840            "screenHeightDp", "I");
1841
1842    return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
1843                                NELEM(gAssetManagerMethods));
1844}
1845
1846}; // namespace android
1847