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