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