blob: 403937be7088bea968d700c5743243849fc9dfdc [file] [log] [blame]
Adam Lesinskidcb3c652017-01-23 12:58:11 -08001/*
2 * Copyright 2006, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080016
17#define LOG_TAG "asset"
18
Dan Albert46d84442014-11-18 16:07:51 -080019#include <inttypes.h>
20#include <linux/capability.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021#include <stdio.h>
Adam Lesinski7fb38312018-01-23 03:17:26 -080022#include <sys/stat.h>
23#include <sys/system_properties.h>
Adam Lesinskidcb3c652017-01-23 12:58:11 -080024#include <sys/types.h>
25#include <sys/wait.h>
Mårten Kongstad48d22322014-01-31 14:43:27 +010026
Dan Albert46d84442014-11-18 16:07:51 -080027#include <private/android_filesystem_config.h> // for AID_SYSTEM
28
Adam Lesinskidcb3c652017-01-23 12:58:11 -080029#include "android-base/logging.h"
30#include "android-base/properties.h"
31#include "android-base/stringprintf.h"
32#include "android_runtime/android_util_AssetManager.h"
Adam Lesinski7fb38312018-01-23 03:17:26 -080033#include "android_runtime/AndroidRuntime.h"
34#include "android_util_Binder.h"
Adam Lesinskidcb3c652017-01-23 12:58:11 -080035#include "androidfw/Asset.h"
36#include "androidfw/AssetManager.h"
37#include "androidfw/AssetManager2.h"
38#include "androidfw/AttributeResolution.h"
39#include "androidfw/MutexGuard.h"
40#include "androidfw/ResourceTypes.h"
Dan Albert3a091b72014-11-20 15:41:25 -080041#include "core_jni_helpers.h"
42#include "jni.h"
Adam Lesinskidcb3c652017-01-23 12:58:11 -080043#include "nativehelper/JNIHelp.h"
44#include "nativehelper/ScopedPrimitiveArray.h"
45#include "nativehelper/ScopedStringChars.h"
46#include "nativehelper/ScopedUtfChars.h"
Dan Albert46d84442014-11-18 16:07:51 -080047#include "utils/Log.h"
Adam Lesinski7fb38312018-01-23 03:17:26 -080048#include "utils/String8.h"
Adam Lesinskidcb3c652017-01-23 12:58:11 -080049#include "utils/misc.h"
Dan Albert46d84442014-11-18 16:07:51 -080050
Mårten Kongstad48d22322014-01-31 14:43:27 +010051extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
52extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
53
Adam Lesinskidcb3c652017-01-23 12:58:11 -080054using ::android::base::StringPrintf;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055
56namespace android {
57
58// ----------------------------------------------------------------------------
59
Adam Lesinskidcb3c652017-01-23 12:58:11 -080060static struct typedvalue_offsets_t {
61 jfieldID mType;
62 jfieldID mData;
63 jfieldID mString;
64 jfieldID mAssetCookie;
65 jfieldID mResourceId;
66 jfieldID mChangingConfigurations;
67 jfieldID mDensity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068} gTypedValueOffsets;
69
Adam Lesinskidcb3c652017-01-23 12:58:11 -080070static struct assetfiledescriptor_offsets_t {
71 jfieldID mFd;
72 jfieldID mStartOffset;
73 jfieldID mLength;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074} gAssetFileDescriptorOffsets;
75
Adam Lesinskidcb3c652017-01-23 12:58:11 -080076static struct assetmanager_offsets_t {
77 jfieldID mObject;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078} gAssetManagerOffsets;
79
Adam Lesinskidcb3c652017-01-23 12:58:11 -080080static struct {
81 jfieldID native_ptr;
82} gApkAssetsFields;
83
84static struct sparsearray_offsets_t {
85 jclass classObject;
86 jmethodID constructor;
87 jmethodID put;
Adam Lesinskide898ff2014-01-29 18:20:45 -080088} gSparseArrayOffsets;
89
Adam Lesinskidcb3c652017-01-23 12:58:11 -080090static struct configuration_offsets_t {
91 jclass classObject;
92 jmethodID constructor;
93 jfieldID mSmallestScreenWidthDpOffset;
94 jfieldID mScreenWidthDpOffset;
95 jfieldID mScreenHeightDpOffset;
Filip Gruszczynski23493322015-07-29 17:02:59 -070096} gConfigurationOffsets;
97
Adam Lesinskidcb3c652017-01-23 12:58:11 -080098jclass g_stringClass = nullptr;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099
100// ----------------------------------------------------------------------------
101
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800102// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
103constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
104 return cookie != kInvalidCookie ? static_cast<jint>(cookie + 1) : -1;
105}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800107constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) {
108 return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109}
110
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900111// This is called by zygote (running as user root) as part of preloadResources.
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800112static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) {
113 switch (pid_t pid = fork()) {
114 case -1:
115 PLOG(ERROR) << "failed to fork for idmap";
116 break;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900117
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800118 // child
119 case 0: {
120 struct __user_cap_header_struct capheader;
121 struct __user_cap_data_struct capdata;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900122
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800123 memset(&capheader, 0, sizeof(capheader));
124 memset(&capdata, 0, sizeof(capdata));
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900125
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800126 capheader.version = _LINUX_CAPABILITY_VERSION;
127 capheader.pid = 0;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900128
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800129 if (capget(&capheader, &capdata) != 0) {
130 PLOG(ERROR) << "capget";
131 exit(1);
132 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900133
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800134 capdata.effective = capdata.permitted;
135 if (capset(&capheader, &capdata) != 0) {
136 PLOG(ERROR) << "capset";
137 exit(1);
138 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900139
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800140 if (setgid(AID_SYSTEM) != 0) {
141 PLOG(ERROR) << "setgid";
142 exit(1);
143 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900144
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800145 if (setuid(AID_SYSTEM) != 0) {
146 PLOG(ERROR) << "setuid";
147 exit(1);
148 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900149
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800150 // Generic idmap parameters
151 const char* argv[8];
152 int argc = 0;
153 struct stat st;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900154
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800155 memset(argv, 0, sizeof(argv));
156 argv[argc++] = AssetManager::IDMAP_BIN;
157 argv[argc++] = "--scan";
158 argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
159 argv[argc++] = AssetManager::TARGET_APK_PATH;
160 argv[argc++] = AssetManager::IDMAP_DIR;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900161
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800162 // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
163 // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
164 std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY,
165 "");
166 if (!overlay_theme_path.empty()) {
167 overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path;
168 if (stat(overlay_theme_path.c_str(), &st) == 0) {
169 argv[argc++] = overlay_theme_path.c_str();
170 }
171 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900172
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800173 if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
174 argv[argc++] = AssetManager::OVERLAY_DIR;
175 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900176
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800177 // Finally, invoke idmap (if any overlay directory exists)
178 if (argc > 5) {
179 execv(AssetManager::IDMAP_BIN, (char* const*)argv);
180 PLOG(ERROR) << "failed to execv for idmap";
181 exit(1); // should never get here
182 } else {
183 exit(0);
184 }
185 } break;
186
187 // parent
188 default:
189 waitpid(pid, nullptr, 0);
190 break;
191 }
192}
193
194static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
195 uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
196 env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
197 env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie,
198 ApkAssetsCookieToJavaCookie(cookie));
199 env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data);
200 env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr);
201 env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref);
202 env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags);
203 if (config != nullptr) {
204 env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density);
205 }
206 return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie));
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900207}
208
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209// ----------------------------------------------------------------------------
210
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800211// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance.
212struct GuardedAssetManager : public ::AAssetManager {
213 Guarded<AssetManager2> guarded_assetmanager;
Adam Lesinskib20a0ce2017-01-23 12:58:11 -0800214};
215
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800216::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
217 jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject);
218 ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle);
219 if (am == nullptr) {
220 jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
221 return nullptr;
222 }
223 return am;
224}
Adam Lesinskib20a0ce2017-01-23 12:58:11 -0800225
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800226Guarded<AssetManager2>* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) {
227 if (assetmanager == nullptr) {
228 return nullptr;
229 }
230 return &reinterpret_cast<GuardedAssetManager*>(assetmanager)->guarded_assetmanager;
231}
Adam Lesinskib20a0ce2017-01-23 12:58:11 -0800232
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800233Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
234 return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager));
235}
Adam Lesinskib20a0ce2017-01-23 12:58:11 -0800236
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800237static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) {
238 return *AssetManagerForNdkAssetManager(reinterpret_cast<AAssetManager*>(ptr));
239}
Adam Lesinskib20a0ce2017-01-23 12:58:11 -0800240
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800241static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
242 jlongArray out_offsets) {
243 off64_t start_offset, length;
244 int fd = asset->openFileDescriptor(&start_offset, &length);
245 asset.reset();
Adam Lesinskib20a0ce2017-01-23 12:58:11 -0800246
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800247 if (fd < 0) {
248 jniThrowException(env, "java/io/FileNotFoundException",
249 "This file can not be opened as a file descriptor; it is probably "
250 "compressed");
251 return nullptr;
252 }
Adam Lesinskib20a0ce2017-01-23 12:58:11 -0800253
Adam Lesinskidcb3c652017-01-23 12:58:11 -0800254 jlong* offsets = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(out_offsets, 0));
255 if (offsets == nullptr) {
256 close(fd);
257 return nullptr;
258 }
259
260 offsets[0] = start_offset;
261 offsets[1] = length;
262
263 env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0);
264
265 jobject file_desc = jniCreateFileDescriptor(env, fd);
266 if (file_desc == nullptr) {
267 close(fd);
268 return nullptr;
269 }
270 return newParcelFileDescriptor(env, file_desc);
271}
272
273static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) {
274 return Asset::getGlobalCount();
275}
276
277static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) {
278 String8 alloc = Asset::getAssetAllocations();
279 if (alloc.length() <= 0) {
280 return nullptr;
281 }
282 return env->NewStringUTF(alloc.string());
283}
284
285static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) {
286 // TODO(adamlesinski): Switch to AssetManager2.
287 return AssetManager::getGlobalCount();
288}
289
290static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) {
291 // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and
292 // AssetManager2 in a contiguous block (GuardedAssetManager).
293 return reinterpret_cast<jlong>(new GuardedAssetManager());
294}
295
296static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
297 delete reinterpret_cast<GuardedAssetManager*>(ptr);
298}
299
300static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
301 jobjectArray apk_assets_array, jboolean invalidate_caches) {
302 const jsize apk_assets_len = env->GetArrayLength(apk_assets_array);
303 std::vector<const ApkAssets*> apk_assets;
304 apk_assets.reserve(apk_assets_len);
305 for (jsize i = 0; i < apk_assets_len; i++) {
306 jobject obj = env->GetObjectArrayElement(apk_assets_array, i);
307 if (obj == nullptr) {
308 std::string msg = StringPrintf("ApkAssets at index %d is null", i);
309 jniThrowNullPointerException(env, msg.c_str());
310 return;
311 }
312
313 jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr);
314 if (env->ExceptionCheck()) {
315 return;
316 }
317 apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr));
318 }
319
320 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
321 assetmanager->SetApkAssets(apk_assets, invalidate_caches);
322}
323
324static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
325 jstring locale, jint orientation, jint touchscreen, jint density,
326 jint keyboard, jint keyboard_hidden, jint navigation,
327 jint screen_width, jint screen_height,
328 jint smallest_screen_width_dp, jint screen_width_dp,
329 jint screen_height_dp, jint screen_layout, jint ui_mode,
330 jint color_mode, jint major_version) {
331 ResTable_config configuration;
332 memset(&configuration, 0, sizeof(configuration));
333 configuration.mcc = static_cast<uint16_t>(mcc);
334 configuration.mnc = static_cast<uint16_t>(mnc);
335 configuration.orientation = static_cast<uint8_t>(orientation);
336 configuration.touchscreen = static_cast<uint8_t>(touchscreen);
337 configuration.density = static_cast<uint16_t>(density);
338 configuration.keyboard = static_cast<uint8_t>(keyboard);
339 configuration.inputFlags = static_cast<uint8_t>(keyboard_hidden);
340 configuration.navigation = static_cast<uint8_t>(navigation);
341 configuration.screenWidth = static_cast<uint16_t>(screen_width);
342 configuration.screenHeight = static_cast<uint16_t>(screen_height);
343 configuration.smallestScreenWidthDp = static_cast<uint16_t>(smallest_screen_width_dp);
344 configuration.screenWidthDp = static_cast<uint16_t>(screen_width_dp);
345 configuration.screenHeightDp = static_cast<uint16_t>(screen_height_dp);
346 configuration.screenLayout = static_cast<uint8_t>(screen_layout);
347 configuration.uiMode = static_cast<uint8_t>(ui_mode);
348 configuration.colorMode = static_cast<uint8_t>(color_mode);
349 configuration.sdkVersion = static_cast<uint16_t>(major_version);
350
351 if (locale != nullptr) {
352 ScopedUtfChars locale_utf8(env, locale);
353 CHECK(locale_utf8.c_str() != nullptr);
354 configuration.setBcp47Locale(locale_utf8.c_str());
355 }
356
357 // Constants duplicated from Java class android.content.res.Configuration.
358 static const jint kScreenLayoutRoundMask = 0x300;
359 static const jint kScreenLayoutRoundShift = 8;
360
361 // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
362 // in C++. We must extract the round qualifier out of the Java screenLayout and put it
363 // into screenLayout2.
364 configuration.screenLayout2 =
365 static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
366
367 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
368 assetmanager->SetConfiguration(configuration);
369}
370
371static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
372 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
373
374 jobject sparse_array =
375 env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor);
376
377 if (sparse_array == nullptr) {
378 // An exception is pending.
379 return nullptr;
380 }
381
382 assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) {
383 jstring jpackage_name = env->NewStringUTF(package_name.c_str());
384 if (jpackage_name == nullptr) {
385 // An exception is pending.
386 return;
387 }
388
389 env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id),
390 jpackage_name);
391 });
392 return sparse_array;
393}
394
395static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) {
396 ScopedUtfChars path_utf8(env, path);
397 if (path_utf8.c_str() == nullptr) {
398 // This will throw NPE.
399 return nullptr;
400 }
401
402 std::vector<std::string> all_file_paths;
403 {
404 StringPiece normalized_path = path_utf8.c_str();
405 if (normalized_path.data()[0] == '/') {
406 normalized_path = normalized_path.substr(1);
407 }
408 std::string root_path = StringPrintf("assets/%s", normalized_path.data());
409 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
410 for (const ApkAssets* assets : assetmanager->GetApkAssets()) {
411 assets->ForEachFile(root_path, [&](const StringPiece& file_path, FileType type) {
412 if (type == FileType::kFileTypeRegular) {
413 all_file_paths.push_back(file_path.to_string());
414 }
415 });
416 }
417 }
418
419 jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr);
420 if (array == nullptr) {
421 return nullptr;
422 }
423
424 jsize index = 0;
425 for (const std::string& file_path : all_file_paths) {
426 jstring java_string = env->NewStringUTF(file_path.c_str());
427
428 // Check for errors creating the strings (if malformed or no memory).
429 if (env->ExceptionCheck()) {
430 return nullptr;
431 }
432
433 env->SetObjectArrayElement(array, index++, java_string);
434
435 // If we have a large amount of string in our array, we might overflow the
436 // local reference table of the VM.
437 env->DeleteLocalRef(java_string);
438 }
439 return array;
440}
441
442static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
443 jint access_mode) {
444 ScopedUtfChars asset_path_utf8(env, asset_path);
445 if (asset_path_utf8.c_str() == nullptr) {
446 // This will throw NPE.
447 return 0;
448 }
449
450 if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
451 access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
452 jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
453 return 0;
454 }
455
456 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
457 std::unique_ptr<Asset> asset =
458 assetmanager->Open(asset_path_utf8.c_str(), static_cast<Asset::AccessMode>(access_mode));
459 if (!asset) {
460 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
461 return 0;
462 }
463 return reinterpret_cast<jlong>(asset.release());
464}
465
466static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
467 jlongArray out_offsets) {
468 ScopedUtfChars asset_path_utf8(env, asset_path);
469 if (asset_path_utf8.c_str() == nullptr) {
470 // This will throw NPE.
471 return nullptr;
472 }
473
474 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
475 std::unique_ptr<Asset> asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
476 if (!asset) {
477 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
478 return nullptr;
479 }
480 return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
481}
482
483static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
484 jstring asset_path, jint access_mode) {
485 ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
486 ScopedUtfChars asset_path_utf8(env, asset_path);
487 if (asset_path_utf8.c_str() == nullptr) {
488 // This will throw NPE.
489 return 0;
490 }
491
492 if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
493 access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
494 jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
495 return 0;
496 }
497
498 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
499 std::unique_ptr<Asset> asset;
500 if (cookie != kInvalidCookie) {
501 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie,
502 static_cast<Asset::AccessMode>(access_mode));
503 } else {
504 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(),
505 static_cast<Asset::AccessMode>(access_mode));
506 }
507
508 if (!asset) {
509 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
510 return 0;
511 }
512 return reinterpret_cast<jlong>(asset.release());
513}
514
515static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
516 jstring asset_path, jlongArray out_offsets) {
517 ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
518 ScopedUtfChars asset_path_utf8(env, asset_path);
519 if (asset_path_utf8.c_str() == nullptr) {
520 // This will throw NPE.
521 return nullptr;
522 }
523
524 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
525 std::unique_ptr<Asset> asset;
526 if (cookie != kInvalidCookie) {
527 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
528 } else {
529 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
530 }
531
532 if (!asset) {
533 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
534 return nullptr;
535 }
536 return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
537}
538
539static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie,
540 jstring asset_path) {
541 ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
542 ScopedUtfChars asset_path_utf8(env, asset_path);
543 if (asset_path_utf8.c_str() == nullptr) {
544 // This will throw NPE.
545 return 0;
546 }
547
548 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
549 std::unique_ptr<Asset> asset;
550 if (cookie != kInvalidCookie) {
551 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
552 } else {
553 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie);
554 }
555
556 if (!asset) {
557 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
558 return 0;
559 }
560
561 // May be nullptr.
562 const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie);
563
564 std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(dynamic_ref_table);
565 status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
566 asset.reset();
567
568 if (err != NO_ERROR) {
569 jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
570 return 0;
571 }
572 return reinterpret_cast<jlong>(xml_tree.release());
573}
574
575static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
576 jshort density, jobject typed_value,
577 jboolean resolve_references) {
578 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
579 Res_value value;
580 ResTable_config selected_config;
581 uint32_t flags;
582 ApkAssetsCookie cookie =
583 assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
584 static_cast<uint16_t>(density), &value, &selected_config, &flags);
585 if (cookie == kInvalidCookie) {
586 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
587 }
588
589 uint32_t ref = static_cast<uint32_t>(resid);
590 if (resolve_references) {
591 cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
592 if (cookie == kInvalidCookie) {
593 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
594 }
595 }
596 return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value);
597}
598
599static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
600 jint bag_entry_id, jobject typed_value) {
601 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
602 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
603 if (bag == nullptr) {
604 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
605 }
606
607 uint32_t type_spec_flags = bag->type_spec_flags;
608 ApkAssetsCookie cookie = kInvalidCookie;
609 const Res_value* bag_value = nullptr;
610 for (const ResolvedBag::Entry& entry : bag) {
611 if (entry.key == static_cast<uint32_t>(bag_entry_id)) {
612 cookie = entry.cookie;
613 bag_value = &entry.value;
614
615 // Keep searching (the old implementation did that).
616 }
617 }
618
619 if (cookie == kInvalidCookie) {
620 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
621 }
622
623 Res_value value = *bag_value;
624 uint32_t ref = static_cast<uint32_t>(resid);
625 ResTable_config selected_config;
626 cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref);
627 if (cookie == kInvalidCookie) {
628 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
629 }
630 return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value);
631}
632
633static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
634 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
635 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
636 if (bag == nullptr) {
637 return nullptr;
638 }
639
640 jintArray array = env->NewIntArray(bag->entry_count);
641 if (env->ExceptionCheck()) {
642 return nullptr;
643 }
644
645 for (uint32_t i = 0; i < bag->entry_count; i++) {
646 jint attr_resid = bag->entries[i].key;
647 env->SetIntArrayRegion(array, i, 1, &attr_resid);
648 }
649 return array;
650}
651
652static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr,
653 jint resid) {
654 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
655 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
656 if (bag == nullptr) {
657 return nullptr;
658 }
659
660 jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr);
661 if (array == nullptr) {
662 return nullptr;
663 }
664
665 for (uint32_t i = 0; i < bag->entry_count; i++) {
666 const ResolvedBag::Entry& entry = bag->entries[i];
667
668 // Resolve any references to their final value.
669 Res_value value = entry.value;
670 ResTable_config selected_config;
671 uint32_t flags;
672 uint32_t ref;
673 ApkAssetsCookie cookie =
674 assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
675 if (cookie == kInvalidCookie) {
676 return nullptr;
677 }
678
679 if (value.dataType == Res_value::TYPE_STRING) {
680 const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie];
681 const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool();
682
683 jstring java_string = nullptr;
684 size_t str_len;
685 const char* str_utf8 = pool->string8At(value.data, &str_len);
686 if (str_utf8 != nullptr) {
687 java_string = env->NewStringUTF(str_utf8);
688 } else {
689 const char16_t* str_utf16 = pool->stringAt(value.data, &str_len);
690 java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16), str_len);
691 }
692
693 // Check for errors creating the strings (if malformed or no memory).
694 if (env->ExceptionCheck()) {
695 return nullptr;
696 }
697
698 env->SetObjectArrayElement(array, i, java_string);
699
700 // If we have a large amount of string in our array, we might overflow the
701 // local reference table of the VM.
702 env->DeleteLocalRef(java_string);
703 }
704 }
705 return array;
706}
707
708static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
709 jint resid) {
710 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
711 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
712 if (bag == nullptr) {
713 return nullptr;
714 }
715
716 jintArray array = env->NewIntArray(bag->entry_count * 2);
717 if (array == nullptr) {
718 return nullptr;
719 }
720
721 jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
722 if (buffer == nullptr) {
723 return nullptr;
724 }
725
726 for (size_t i = 0; i < bag->entry_count; i++) {
727 const ResolvedBag::Entry& entry = bag->entries[i];
728 Res_value value = entry.value;
729 ResTable_config selected_config;
730 uint32_t flags;
731 uint32_t ref;
732 ApkAssetsCookie cookie =
733 assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
734 if (cookie == kInvalidCookie) {
735 env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
736 return nullptr;
737 }
738
739 jint string_index = -1;
740 if (value.dataType == Res_value::TYPE_STRING) {
741 string_index = static_cast<jint>(value.data);
742 }
743
744 buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie);
745 buffer[(i * 2) + 1] = string_index;
746 }
747 env->ReleasePrimitiveArrayCritical(array, buffer, 0);
748 return array;
749}
750
751static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
752 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
753 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
754 if (bag == nullptr) {
755 return nullptr;
756 }
757
758 jintArray array = env->NewIntArray(bag->entry_count);
759 if (array == nullptr) {
760 return nullptr;
761 }
762
763 jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
764 if (buffer == nullptr) {
765 return nullptr;
766 }
767
768 for (size_t i = 0; i < bag->entry_count; i++) {
769 const ResolvedBag::Entry& entry = bag->entries[i];
770 Res_value value = entry.value;
771 ResTable_config selected_config;
772 uint32_t flags;
773 uint32_t ref;
774 ApkAssetsCookie cookie =
775 assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
776 if (cookie == kInvalidCookie) {
777 env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
778 return nullptr;
779 }
780
781 if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) {
782 buffer[i] = static_cast<jint>(value.data);
783 }
784 }
785 env->ReleasePrimitiveArrayCritical(array, buffer, 0);
786 return array;
787}
788
789static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) {
790 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
791 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
792 if (bag == nullptr) {
793 return -1;
794 }
795 return static_cast<jint>(bag->entry_count);
796}
797
798static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
799 jintArray out_data) {
800 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
801 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
802 if (bag == nullptr) {
803 return -1;
804 }
805
806 const jsize out_data_length = env->GetArrayLength(out_data);
807 if (env->ExceptionCheck()) {
808 return -1;
809 }
810
811 if (static_cast<jsize>(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) {
812 jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough");
813 return -1;
814 }
815
816 jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_data, nullptr));
817 if (buffer == nullptr) {
818 return -1;
819 }
820
821 jint* cursor = buffer;
822 for (size_t i = 0; i < bag->entry_count; i++) {
823 const ResolvedBag::Entry& entry = bag->entries[i];
824 Res_value value = entry.value;
825 ResTable_config selected_config;
826 selected_config.density = 0;
827 uint32_t flags = bag->type_spec_flags;
828 uint32_t ref;
829 ApkAssetsCookie cookie =
830 assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
831 if (cookie == kInvalidCookie) {
832 env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT);
833 return -1;
834 }
835
836 // Deal with the special @null value -- it turns back to TYPE_NULL.
837 if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
838 value.dataType = Res_value::TYPE_NULL;
839 value.data = Res_value::DATA_NULL_UNDEFINED;
840 }
841
842 cursor[STYLE_TYPE] = static_cast<jint>(value.dataType);
843 cursor[STYLE_DATA] = static_cast<jint>(value.data);
844 cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
845 cursor[STYLE_RESOURCE_ID] = static_cast<jint>(ref);
846 cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(flags);
847 cursor[STYLE_DENSITY] = static_cast<jint>(selected_config.density);
848 cursor += STYLE_NUM_ENTRIES;
849 }
850 env->ReleasePrimitiveArrayCritical(out_data, buffer, 0);
851 return static_cast<jint>(bag->entry_count);
852}
853
854static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name,
855 jstring def_type, jstring def_package) {
856 ScopedUtfChars name_utf8(env, name);
857 if (name_utf8.c_str() == nullptr) {
858 // This will throw NPE.
859 return 0;
860 }
861
862 std::string type;
863 if (def_type != nullptr) {
864 ScopedUtfChars type_utf8(env, def_type);
865 CHECK(type_utf8.c_str() != nullptr);
866 type = type_utf8.c_str();
867 }
868
869 std::string package;
870 if (def_package != nullptr) {
871 ScopedUtfChars package_utf8(env, def_package);
872 CHECK(package_utf8.c_str() != nullptr);
873 package = package_utf8.c_str();
874 }
875 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
876 return static_cast<jint>(assetmanager->GetResourceId(name_utf8.c_str(), type, package));
877}
878
879static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
880 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
881 AssetManager2::ResourceName name;
882 if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
883 return nullptr;
884 }
885
886 std::string result;
887 if (name.package != nullptr) {
888 result.append(name.package, name.package_len);
889 }
890
891 if (name.type != nullptr || name.type16 != nullptr) {
892 if (!result.empty()) {
893 result += ":";
894 }
895
896 if (name.type != nullptr) {
897 result.append(name.type, name.type_len);
898 } else {
899 result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len));
900 }
901 }
902
903 if (name.entry != nullptr || name.entry16 != nullptr) {
904 if (!result.empty()) {
905 result += "/";
906 }
907
908 if (name.entry != nullptr) {
909 result.append(name.entry, name.entry_len);
910 } else {
911 result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
912 }
913 }
914 return env->NewStringUTF(result.c_str());
915}
916
917static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
918 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
919 AssetManager2::ResourceName name;
920 if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
921 return nullptr;
922 }
923
924 if (name.package != nullptr) {
925 return env->NewStringUTF(name.package);
926 }
927 return nullptr;
928}
929
930static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
931 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
932 AssetManager2::ResourceName name;
933 if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
934 return nullptr;
935 }
936
937 if (name.type != nullptr) {
938 return env->NewStringUTF(name.type);
939 } else if (name.type16 != nullptr) {
940 return env->NewString(reinterpret_cast<const jchar*>(name.type16), name.type_len);
941 }
942 return nullptr;
943}
944
945static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
946 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
947 AssetManager2::ResourceName name;
948 if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
949 return nullptr;
950 }
951
952 if (name.entry != nullptr) {
953 return env->NewStringUTF(name.entry);
954 } else if (name.entry16 != nullptr) {
955 return env->NewString(reinterpret_cast<const jchar*>(name.entry16), name.entry_len);
956 }
957 return nullptr;
958}
959
960static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr,
961 jboolean exclude_system) {
962 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
963 std::set<std::string> locales =
964 assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/);
965
966 jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr);
967 if (array == nullptr) {
968 return nullptr;
969 }
970
971 size_t idx = 0;
972 for (const std::string& locale : locales) {
973 jstring java_string = env->NewStringUTF(locale.c_str());
974 if (java_string == nullptr) {
975 return nullptr;
976 }
977 env->SetObjectArrayElement(array, idx++, java_string);
978 env->DeleteLocalRef(java_string);
979 }
980 return array;
981}
982
983static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
984 jobject result =
985 env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor);
986 if (result == nullptr) {
987 return nullptr;
988 }
989
990 env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset,
991 config.smallestScreenWidthDp);
992 env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
993 env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
994 return result;
995}
996
997static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
998 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
999 std::set<ResTable_config> configurations =
1000 assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/);
1001
1002 jobjectArray array =
1003 env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr);
1004 if (array == nullptr) {
1005 return nullptr;
1006 }
1007
1008 size_t idx = 0;
1009 for (const ResTable_config& configuration : configurations) {
1010 jobject java_configuration = ConstructConfigurationObject(env, configuration);
1011 if (java_configuration == nullptr) {
1012 return nullptr;
1013 }
1014
1015 env->SetObjectArrayElement(array, idx++, java_configuration);
1016 env->DeleteLocalRef(java_configuration);
1017 }
1018 return array;
1019}
1020
1021static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1022 jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr,
1023 jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) {
1024 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1025 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1026 CHECK(theme->GetAssetManager() == &(*assetmanager));
1027 (void) assetmanager;
1028
1029 ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
1030 uint32_t* out_values = reinterpret_cast<uint32_t*>(out_values_ptr);
1031 uint32_t* out_indices = reinterpret_cast<uint32_t*>(out_indices_ptr);
1032
1033 jsize attrs_len = env->GetArrayLength(java_attrs);
1034 jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
1035 if (attrs == nullptr) {
1036 return;
1037 }
1038
1039 ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr),
1040 static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(attrs), attrs_len,
1041 out_values, out_indices);
1042 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1043}
1044
1045static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1046 jint def_style_attr, jint def_style_resid, jintArray java_values,
1047 jintArray java_attrs, jintArray out_java_values,
1048 jintArray out_java_indices) {
1049 const jsize attrs_len = env->GetArrayLength(java_attrs);
1050 const jsize out_values_len = env->GetArrayLength(out_java_values);
1051 if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
1052 jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
1053 return JNI_FALSE;
1054 }
1055
1056 jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
1057 if (attrs == nullptr) {
1058 return JNI_FALSE;
1059 }
1060
1061 jint* values = nullptr;
1062 jsize values_len = 0;
1063 if (java_values != nullptr) {
1064 values_len = env->GetArrayLength(java_values);
1065 values = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_values, nullptr));
1066 if (values == nullptr) {
1067 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1068 return JNI_FALSE;
1069 }
1070 }
1071
1072 jint* out_values =
1073 reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
1074 if (out_values == nullptr) {
1075 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1076 if (values != nullptr) {
1077 env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
1078 }
1079 return JNI_FALSE;
1080 }
1081
1082 jint* out_indices = nullptr;
1083 if (out_java_indices != nullptr) {
1084 jsize out_indices_len = env->GetArrayLength(out_java_indices);
1085 if (out_indices_len > attrs_len) {
1086 out_indices =
1087 reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
1088 if (out_indices == nullptr) {
1089 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1090 if (values != nullptr) {
1091 env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
1092 }
1093 env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
1094 return JNI_FALSE;
1095 }
1096 }
1097 }
1098
1099 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1100 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1101 CHECK(theme->GetAssetManager() == &(*assetmanager));
1102 (void) assetmanager;
1103
1104 bool result = ResolveAttrs(
1105 theme, static_cast<uint32_t>(def_style_attr), static_cast<uint32_t>(def_style_resid),
1106 reinterpret_cast<uint32_t*>(values), values_len, reinterpret_cast<uint32_t*>(attrs),
1107 attrs_len, reinterpret_cast<uint32_t*>(out_values), reinterpret_cast<uint32_t*>(out_indices));
1108 if (out_indices != nullptr) {
1109 env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
1110 }
1111
1112 env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
1113 if (values != nullptr) {
1114 env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
1115 }
1116 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1117 return result ? JNI_TRUE : JNI_FALSE;
1118}
1119
1120static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr,
1121 jlong xml_parser_ptr, jintArray java_attrs,
1122 jintArray out_java_values, jintArray out_java_indices) {
1123 const jsize attrs_len = env->GetArrayLength(java_attrs);
1124 const jsize out_values_len = env->GetArrayLength(out_java_values);
1125 if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
1126 jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
1127 return JNI_FALSE;
1128 }
1129
1130 jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
1131 if (attrs == nullptr) {
1132 return JNI_FALSE;
1133 }
1134
1135 jint* out_values =
1136 reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
1137 if (out_values == nullptr) {
1138 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1139 return JNI_FALSE;
1140 }
1141
1142 jint* out_indices = nullptr;
1143 if (out_java_indices != nullptr) {
1144 jsize out_indices_len = env->GetArrayLength(out_java_indices);
1145 if (out_indices_len > attrs_len) {
1146 out_indices =
1147 reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
1148 if (out_indices == nullptr) {
1149 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1150 env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
1151 return JNI_FALSE;
1152 }
1153 }
1154 }
1155
1156 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1157 ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
1158
1159 bool result = RetrieveAttributes(assetmanager.get(), xml_parser,
1160 reinterpret_cast<uint32_t*>(attrs), attrs_len,
1161 reinterpret_cast<uint32_t*>(out_values),
1162 reinterpret_cast<uint32_t*>(out_indices));
1163
1164 if (out_indices != nullptr) {
1165 env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
1166 }
1167 env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
1168 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1169 return result ? JNI_TRUE : JNI_FALSE;
1170}
1171
1172static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
1173 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1174 return reinterpret_cast<jlong>(assetmanager->NewTheme().release());
1175}
1176
1177static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
1178 delete reinterpret_cast<Theme*>(theme_ptr);
1179}
1180
1181static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1182 jint resid, jboolean force) {
1183 // AssetManager is accessed via the theme, so grab an explicit lock here.
1184 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1185 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1186 CHECK(theme->GetAssetManager() == &(*assetmanager));
1187 (void) assetmanager;
1188 theme->ApplyStyle(static_cast<uint32_t>(resid), force);
1189
1190 // TODO(adamlesinski): Consider surfacing exception when result is failure.
1191 // CTS currently expects no exceptions from this method.
1192 // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid);
1193 // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
1194}
1195
1196static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr,
1197 jlong src_theme_ptr) {
1198 Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
1199 Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr);
1200 if (!dst_theme->SetTo(*src_theme)) {
1201 jniThrowException(env, "java/lang/IllegalArgumentException",
1202 "Themes are from different AssetManagers");
1203 }
1204}
1205
1206static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
1207 reinterpret_cast<Theme*>(theme_ptr)->Clear();
1208}
1209
1210static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1211 jint resid, jobject typed_value,
1212 jboolean resolve_references) {
1213 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1214 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1215 CHECK(theme->GetAssetManager() == &(*assetmanager));
1216 (void) assetmanager;
1217
1218 Res_value value;
1219 uint32_t flags;
1220 ApkAssetsCookie cookie = theme->GetAttribute(static_cast<uint32_t>(resid), &value, &flags);
1221 if (cookie == kInvalidCookie) {
1222 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
1223 }
1224
1225 uint32_t ref = 0u;
1226 if (resolve_references) {
1227 ResTable_config selected_config;
1228 cookie =
1229 theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
1230 if (cookie == kInvalidCookie) {
1231 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
1232 }
1233 }
1234 return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value);
1235}
1236
1237static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1238 jint priority, jstring tag, jstring prefix) {
1239 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1240 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1241 CHECK(theme->GetAssetManager() == &(*assetmanager));
1242 (void) assetmanager;
1243 (void) theme;
1244 (void) priority;
1245 (void) tag;
1246 (void) prefix;
1247}
1248
1249static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/,
1250 jlong theme_ptr) {
1251 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1252 return static_cast<jint>(theme->GetChangingConfigurations());
1253}
1254
1255static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
1256 delete reinterpret_cast<Asset*>(asset_ptr);
1257}
1258
1259static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
1260 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1261 uint8_t b;
1262 ssize_t res = asset->read(&b, sizeof(b));
1263 return res == sizeof(b) ? static_cast<jint>(b) : -1;
1264}
1265
1266static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer,
1267 jint offset, jint len) {
1268 if (len == 0) {
1269 return 0;
1270 }
1271
1272 jsize buffer_len = env->GetArrayLength(java_buffer);
1273 if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len ||
1274 offset > buffer_len - len) {
1275 jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
1276 return -1;
1277 }
1278
1279 ScopedByteArrayRW byte_array(env, java_buffer);
1280 if (byte_array.get() == nullptr) {
1281 return -1;
1282 }
1283
1284 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1285 ssize_t res = asset->read(byte_array.get() + offset, len);
1286 if (res < 0) {
1287 jniThrowException(env, "java/io/IOException", "");
1288 return -1;
1289 }
1290 return res > 0 ? static_cast<jint>(res) : -1;
1291}
1292
1293static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset,
1294 jint whence) {
1295 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1296 return static_cast<jlong>(asset->seek(
1297 static_cast<off64_t>(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR))));
1298}
1299
1300static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
1301 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1302 return static_cast<jlong>(asset->getLength());
1303}
1304
1305static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
1306 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1307 return static_cast<jlong>(asset->getRemainingLength());
1308}
1309
1310// ----------------------------------------------------------------------------
1311
1312// JNI registration.
1313static const JNINativeMethod gAssetManagerMethods[] = {
1314 // AssetManager setup methods.
1315 {"nativeCreate", "()J", (void*)NativeCreate},
1316 {"nativeDestroy", "(J)V", (void*)NativeDestroy},
1317 {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
1318 {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V",
1319 (void*)NativeSetConfiguration},
1320 {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;",
1321 (void*)NativeGetAssignedPackageIdentifiers},
1322
1323 // AssetManager file methods.
1324 {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
1325 {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
1326 {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1327 (void*)NativeOpenAssetFd},
1328 {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset},
1329 {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1330 (void*)NativeOpenNonAssetFd},
1331 {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
1332
1333 // AssetManager resource methods.
1334 {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue},
1335 {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I",
1336 (void*)NativeGetResourceBagValue},
1337 {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes},
1338 {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;",
1339 (void*)NativeGetResourceStringArray},
1340 {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo},
1341 {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
1342 {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
1343 {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
1344
1345 // AssetManager resource name/ID methods.
1346 {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
1347 (void*)NativeGetResourceIdentifier},
1348 {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName},
1349 {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName},
1350 {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
1351 {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
1352 {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
1353 {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
1354 (void*)NativeGetSizeConfigurations},
1355
1356 // Style attribute related methods.
1357 {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
1358 {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
1359 {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
1360
1361 // Theme related methods.
1362 {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
1363 {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy},
1364 {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
1365 {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy},
1366 {"nativeThemeClear", "(J)V", (void*)NativeThemeClear},
1367 {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
1368 (void*)NativeThemeGetAttributeValue},
1369 {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
1370 {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations},
1371
1372 // AssetInputStream methods.
1373 {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy},
1374 {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar},
1375 {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead},
1376 {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek},
1377 {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength},
1378 {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
1379
1380 // System/idmap related methods.
1381 {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps},
1382
1383 // Global management/debug methods.
1384 {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
1385 {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations},
1386 {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount},
1387};
1388
1389int register_android_content_AssetManager(JNIEnv* env) {
1390 jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets");
1391 gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J");
1392
1393 jclass typedValue = FindClassOrDie(env, "android/util/TypedValue");
1394 gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I");
1395 gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I");
1396 gTypedValueOffsets.mString =
1397 GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;");
1398 gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I");
1399 gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I");
1400 gTypedValueOffsets.mChangingConfigurations =
1401 GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I");
1402 gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
1403
1404 jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
1405 gAssetFileDescriptorOffsets.mFd =
1406 GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
1407 gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
1408 gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
1409
1410 jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
1411 gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
1412
1413 jclass stringClass = FindClassOrDie(env, "java/lang/String");
1414 g_stringClass = MakeGlobalRefOrDie(env, stringClass);
1415
1416 jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
1417 gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
1418 gSparseArrayOffsets.constructor =
1419 GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "<init>", "()V");
1420 gSparseArrayOffsets.put =
1421 GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V");
1422
1423 jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
1424 gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
1425 gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "<init>", "()V");
1426 gConfigurationOffsets.mSmallestScreenWidthDpOffset =
1427 GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I");
1428 gConfigurationOffsets.mScreenWidthDpOffset =
1429 GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I");
1430 gConfigurationOffsets.mScreenHeightDpOffset =
1431 GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I");
1432
1433 return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
1434 NELEM(gAssetManagerMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001435}
1436
1437}; // namespace android