/*
|
* Copyright 2006, The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
#define ATRACE_TAG ATRACE_TAG_RESOURCES
|
#define LOG_TAG "asset"
|
|
#include <inttypes.h>
|
#include <linux/capability.h>
|
#include <stdio.h>
|
#include <sys/stat.h>
|
#include <sys/system_properties.h>
|
#include <sys/types.h>
|
#include <sys/wait.h>
|
#include <unistd.h>
|
|
#include <private/android_filesystem_config.h> // for AID_SYSTEM
|
|
#include <sstream>
|
#include <string>
|
|
#include "android-base/logging.h"
|
#include "android-base/properties.h"
|
#include "android-base/stringprintf.h"
|
#include "android_runtime/android_util_AssetManager.h"
|
#include "android_runtime/AndroidRuntime.h"
|
#include "android_util_Binder.h"
|
#include "androidfw/Asset.h"
|
#include "androidfw/AssetManager.h"
|
#include "androidfw/AssetManager2.h"
|
#include "androidfw/AttributeResolution.h"
|
#include "androidfw/MutexGuard.h"
|
#include "androidfw/PosixUtils.h"
|
#include "androidfw/ResourceTypes.h"
|
#include "androidfw/ResourceUtils.h"
|
|
#include "core_jni_helpers.h"
|
#include "jni.h"
|
#include "nativehelper/JNIHelp.h"
|
#include "nativehelper/ScopedPrimitiveArray.h"
|
#include "nativehelper/ScopedStringChars.h"
|
#include "nativehelper/ScopedUtfChars.h"
|
#include "utils/Log.h"
|
#include "utils/misc.h"
|
#include "utils/String8.h"
|
#include "utils/Trace.h"
|
|
extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
|
extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
|
|
using ::android::base::StringPrintf;
|
using ::android::util::ExecuteBinary;
|
|
namespace android {
|
|
// ----------------------------------------------------------------------------
|
|
static struct typedvalue_offsets_t {
|
jfieldID mType;
|
jfieldID mData;
|
jfieldID mString;
|
jfieldID mAssetCookie;
|
jfieldID mResourceId;
|
jfieldID mChangingConfigurations;
|
jfieldID mDensity;
|
} gTypedValueOffsets;
|
|
static struct assetfiledescriptor_offsets_t {
|
jfieldID mFd;
|
jfieldID mStartOffset;
|
jfieldID mLength;
|
} gAssetFileDescriptorOffsets;
|
|
// This is also used by asset_manager.cpp.
|
assetmanager_offsets_t gAssetManagerOffsets;
|
|
static struct {
|
jfieldID native_ptr;
|
} gApkAssetsFields;
|
|
static struct sparsearray_offsets_t {
|
jclass classObject;
|
jmethodID constructor;
|
jmethodID put;
|
} gSparseArrayOffsets;
|
|
static struct configuration_offsets_t {
|
jclass classObject;
|
jmethodID constructor;
|
jfieldID mSmallestScreenWidthDpOffset;
|
jfieldID mScreenWidthDpOffset;
|
jfieldID mScreenHeightDpOffset;
|
} gConfigurationOffsets;
|
|
static struct arraymap_offsets_t {
|
jclass classObject;
|
jmethodID constructor;
|
jmethodID put;
|
} gArrayMapOffsets;
|
|
jclass g_stringClass = nullptr;
|
|
// ----------------------------------------------------------------------------
|
|
// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
|
constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
|
return cookie != kInvalidCookie ? static_cast<jint>(cookie + 1) : -1;
|
}
|
|
constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) {
|
return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie;
|
}
|
|
// This is called by zygote (running as user root) as part of preloadResources.
|
static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) {
|
switch (pid_t pid = fork()) {
|
case -1:
|
PLOG(ERROR) << "failed to fork for idmap";
|
break;
|
|
// child
|
case 0: {
|
struct __user_cap_header_struct capheader;
|
struct __user_cap_data_struct capdata;
|
|
memset(&capheader, 0, sizeof(capheader));
|
memset(&capdata, 0, sizeof(capdata));
|
|
capheader.version = _LINUX_CAPABILITY_VERSION;
|
capheader.pid = 0;
|
|
if (capget(&capheader, &capdata) != 0) {
|
PLOG(ERROR) << "capget";
|
exit(1);
|
}
|
|
capdata.effective = capdata.permitted;
|
if (capset(&capheader, &capdata) != 0) {
|
PLOG(ERROR) << "capset";
|
exit(1);
|
}
|
|
if (setgid(AID_SYSTEM) != 0) {
|
PLOG(ERROR) << "setgid";
|
exit(1);
|
}
|
|
if (setuid(AID_SYSTEM) != 0) {
|
PLOG(ERROR) << "setuid";
|
exit(1);
|
}
|
|
// Generic idmap parameters
|
const char* argv[10];
|
int argc = 0;
|
struct stat st;
|
|
memset(argv, 0, sizeof(argv));
|
argv[argc++] = AssetManager::IDMAP_BIN;
|
argv[argc++] = "--scan";
|
argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
|
argv[argc++] = AssetManager::TARGET_APK_PATH;
|
argv[argc++] = AssetManager::IDMAP_DIR;
|
|
// Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
|
// use VENDOR_OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in
|
// addition to VENDOR_OVERLAY_DIR.
|
std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY,
|
"");
|
if (!overlay_theme_path.empty()) {
|
overlay_theme_path =
|
std::string(AssetManager::VENDOR_OVERLAY_DIR) + "/" + overlay_theme_path;
|
if (stat(overlay_theme_path.c_str(), &st) == 0) {
|
argv[argc++] = overlay_theme_path.c_str();
|
}
|
}
|
|
if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
|
argv[argc++] = AssetManager::VENDOR_OVERLAY_DIR;
|
}
|
|
if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
|
argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR;
|
}
|
|
if (stat(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR, &st) == 0) {
|
argv[argc++] = AssetManager::PRODUCT_SERVICES_OVERLAY_DIR;
|
}
|
|
if (stat(AssetManager::ODM_OVERLAY_DIR, &st) == 0) {
|
argv[argc++] = AssetManager::ODM_OVERLAY_DIR;
|
}
|
|
if (stat(AssetManager::OEM_OVERLAY_DIR, &st) == 0) {
|
argv[argc++] = AssetManager::OEM_OVERLAY_DIR;
|
}
|
|
// Finally, invoke idmap (if any overlay directory exists)
|
if (argc > 5) {
|
execv(AssetManager::IDMAP_BIN, (char* const*)argv);
|
PLOG(ERROR) << "failed to execv for idmap";
|
exit(1); // should never get here
|
} else {
|
exit(0);
|
}
|
} break;
|
|
// parent
|
default:
|
waitpid(pid, nullptr, 0);
|
break;
|
}
|
}
|
|
static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* env,
|
jclass /*clazz*/) {
|
// --input-directory can be given multiple times, but idmap2 expects the directory to exist
|
std::vector<std::string> input_dirs;
|
struct stat st;
|
if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
|
input_dirs.push_back(AssetManager::VENDOR_OVERLAY_DIR);
|
}
|
|
if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
|
input_dirs.push_back(AssetManager::PRODUCT_OVERLAY_DIR);
|
}
|
|
if (stat(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR, &st) == 0) {
|
input_dirs.push_back(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR);
|
}
|
|
if (stat(AssetManager::ODM_OVERLAY_DIR, &st) == 0) {
|
input_dirs.push_back(AssetManager::ODM_OVERLAY_DIR);
|
}
|
|
if (stat(AssetManager::OEM_OVERLAY_DIR, &st) == 0) {
|
input_dirs.push_back(AssetManager::OEM_OVERLAY_DIR);
|
}
|
|
if (input_dirs.empty()) {
|
LOG(WARNING) << "no directories for idmap2 to scan";
|
return env->NewObjectArray(0, g_stringClass, nullptr);
|
}
|
|
if (access("/system/bin/idmap2", X_OK) == -1) {
|
PLOG(WARNING) << "unable to execute idmap2";
|
return nullptr;
|
}
|
|
std::vector<std::string> argv{"/system/bin/idmap2",
|
"scan",
|
"--recursive",
|
"--target-package-name", "android",
|
"--target-apk-path", "/system/framework/framework-res.apk",
|
"--output-directory", "/data/resource-cache"};
|
|
for (const auto& dir : input_dirs) {
|
argv.push_back("--input-directory");
|
argv.push_back(dir);
|
}
|
|
const auto result = ExecuteBinary(argv);
|
|
if (!result) {
|
LOG(ERROR) << "failed to execute idmap2";
|
return nullptr;
|
}
|
|
if (result->status != 0) {
|
LOG(ERROR) << "idmap2: " << result->stderr;
|
return nullptr;
|
}
|
|
std::vector<std::string> idmap_paths;
|
std::istringstream input(result->stdout);
|
std::string path;
|
while (std::getline(input, path)) {
|
idmap_paths.push_back(path);
|
}
|
|
jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr);
|
if (array == nullptr) {
|
return nullptr;
|
}
|
for (size_t i = 0; i < idmap_paths.size(); i++) {
|
const std::string path = idmap_paths[i];
|
jstring java_string = env->NewStringUTF(path.c_str());
|
if (env->ExceptionCheck()) {
|
return nullptr;
|
}
|
env->SetObjectArrayElement(array, i, java_string);
|
env->DeleteLocalRef(java_string);
|
}
|
return array;
|
}
|
|
static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
|
uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
|
env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
|
env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie,
|
ApkAssetsCookieToJavaCookie(cookie));
|
env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data);
|
env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr);
|
env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref);
|
env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags);
|
if (config != nullptr) {
|
env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density);
|
}
|
return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie));
|
}
|
|
// ----------------------------------------------------------------------------
|
|
// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance.
|
struct GuardedAssetManager : public ::AAssetManager {
|
Guarded<AssetManager2> guarded_assetmanager;
|
};
|
|
::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
|
jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject);
|
::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle);
|
if (am == nullptr) {
|
jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
|
return nullptr;
|
}
|
return am;
|
}
|
|
Guarded<AssetManager2>* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) {
|
if (assetmanager == nullptr) {
|
return nullptr;
|
}
|
return &reinterpret_cast<GuardedAssetManager*>(assetmanager)->guarded_assetmanager;
|
}
|
|
Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
|
return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager));
|
}
|
|
static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) {
|
return *AssetManagerForNdkAssetManager(reinterpret_cast<AAssetManager*>(ptr));
|
}
|
|
static jobject NativeGetOverlayableMap(JNIEnv* env, jclass /*clazz*/, jlong ptr,
|
jstring package_name) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
const ScopedUtfChars package_name_utf8(env, package_name);
|
CHECK(package_name_utf8.c_str() != nullptr);
|
const std::string std_package_name(package_name_utf8.c_str());
|
const std::unordered_map<std::string, std::string>* map = nullptr;
|
|
assetmanager->ForEachPackage([&](const std::string& this_package_name, uint8_t package_id) {
|
if (this_package_name == std_package_name) {
|
map = assetmanager->GetOverlayableMapForPackage(package_id);
|
return false;
|
}
|
return true;
|
});
|
|
if (map == nullptr) {
|
return nullptr;
|
}
|
|
jobject array_map = env->NewObject(gArrayMapOffsets.classObject, gArrayMapOffsets.constructor);
|
if (array_map == nullptr) {
|
return nullptr;
|
}
|
|
for (const auto& iter : *map) {
|
jstring name = env->NewStringUTF(iter.first.c_str());
|
if (env->ExceptionCheck()) {
|
return nullptr;
|
}
|
|
jstring actor = env->NewStringUTF(iter.second.c_str());
|
if (env->ExceptionCheck()) {
|
env->DeleteLocalRef(name);
|
return nullptr;
|
}
|
|
env->CallObjectMethod(array_map, gArrayMapOffsets.put, name, actor);
|
|
env->DeleteLocalRef(name);
|
env->DeleteLocalRef(actor);
|
}
|
|
return array_map;
|
}
|
|
static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
|
jlongArray out_offsets) {
|
off64_t start_offset, length;
|
int fd = asset->openFileDescriptor(&start_offset, &length);
|
asset.reset();
|
|
if (fd < 0) {
|
jniThrowException(env, "java/io/FileNotFoundException",
|
"This file can not be opened as a file descriptor; it is probably "
|
"compressed");
|
return nullptr;
|
}
|
|
jlong* offsets = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(out_offsets, 0));
|
if (offsets == nullptr) {
|
close(fd);
|
return nullptr;
|
}
|
|
offsets[0] = start_offset;
|
offsets[1] = length;
|
|
env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0);
|
|
jobject file_desc = jniCreateFileDescriptor(env, fd);
|
if (file_desc == nullptr) {
|
close(fd);
|
return nullptr;
|
}
|
return newParcelFileDescriptor(env, file_desc);
|
}
|
|
static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) {
|
return Asset::getGlobalCount();
|
}
|
|
static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) {
|
String8 alloc = Asset::getAssetAllocations();
|
if (alloc.length() <= 0) {
|
return nullptr;
|
}
|
return env->NewStringUTF(alloc.string());
|
}
|
|
static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) {
|
// TODO(adamlesinski): Switch to AssetManager2.
|
return AssetManager::getGlobalCount();
|
}
|
|
static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) {
|
// AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and
|
// AssetManager2 in a contiguous block (GuardedAssetManager).
|
return reinterpret_cast<jlong>(new GuardedAssetManager());
|
}
|
|
static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
|
delete reinterpret_cast<GuardedAssetManager*>(ptr);
|
}
|
|
static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
|
jobjectArray apk_assets_array, jboolean invalidate_caches) {
|
ATRACE_NAME("AssetManager::SetApkAssets");
|
|
const jsize apk_assets_len = env->GetArrayLength(apk_assets_array);
|
std::vector<const ApkAssets*> apk_assets;
|
apk_assets.reserve(apk_assets_len);
|
for (jsize i = 0; i < apk_assets_len; i++) {
|
jobject obj = env->GetObjectArrayElement(apk_assets_array, i);
|
if (obj == nullptr) {
|
std::string msg = StringPrintf("ApkAssets at index %d is null", i);
|
jniThrowNullPointerException(env, msg.c_str());
|
return;
|
}
|
|
jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr);
|
if (env->ExceptionCheck()) {
|
return;
|
}
|
apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr));
|
}
|
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
assetmanager->SetApkAssets(apk_assets, invalidate_caches);
|
}
|
|
static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
|
jstring locale, jint orientation, jint touchscreen, jint density,
|
jint keyboard, jint keyboard_hidden, jint navigation,
|
jint screen_width, jint screen_height,
|
jint smallest_screen_width_dp, jint screen_width_dp,
|
jint screen_height_dp, jint screen_layout, jint ui_mode,
|
jint color_mode, jint major_version) {
|
ATRACE_NAME("AssetManager::SetConfiguration");
|
|
ResTable_config configuration;
|
memset(&configuration, 0, sizeof(configuration));
|
configuration.mcc = static_cast<uint16_t>(mcc);
|
configuration.mnc = static_cast<uint16_t>(mnc);
|
configuration.orientation = static_cast<uint8_t>(orientation);
|
configuration.touchscreen = static_cast<uint8_t>(touchscreen);
|
configuration.density = static_cast<uint16_t>(density);
|
configuration.keyboard = static_cast<uint8_t>(keyboard);
|
configuration.inputFlags = static_cast<uint8_t>(keyboard_hidden);
|
configuration.navigation = static_cast<uint8_t>(navigation);
|
configuration.screenWidth = static_cast<uint16_t>(screen_width);
|
configuration.screenHeight = static_cast<uint16_t>(screen_height);
|
configuration.smallestScreenWidthDp = static_cast<uint16_t>(smallest_screen_width_dp);
|
configuration.screenWidthDp = static_cast<uint16_t>(screen_width_dp);
|
configuration.screenHeightDp = static_cast<uint16_t>(screen_height_dp);
|
configuration.screenLayout = static_cast<uint8_t>(screen_layout);
|
configuration.uiMode = static_cast<uint8_t>(ui_mode);
|
configuration.colorMode = static_cast<uint8_t>(color_mode);
|
configuration.sdkVersion = static_cast<uint16_t>(major_version);
|
|
if (locale != nullptr) {
|
ScopedUtfChars locale_utf8(env, locale);
|
CHECK(locale_utf8.c_str() != nullptr);
|
configuration.setBcp47Locale(locale_utf8.c_str());
|
}
|
|
// Constants duplicated from Java class android.content.res.Configuration.
|
static const jint kScreenLayoutRoundMask = 0x300;
|
static const jint kScreenLayoutRoundShift = 8;
|
|
// In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
|
// in C++. We must extract the round qualifier out of the Java screenLayout and put it
|
// into screenLayout2.
|
configuration.screenLayout2 =
|
static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
|
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
assetmanager->SetConfiguration(configuration);
|
}
|
|
static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
|
jobject sparse_array =
|
env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor);
|
|
if (sparse_array == nullptr) {
|
// An exception is pending.
|
return nullptr;
|
}
|
|
assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) -> bool {
|
jstring jpackage_name = env->NewStringUTF(package_name.c_str());
|
if (jpackage_name == nullptr) {
|
// An exception is pending.
|
return false;
|
}
|
|
env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id),
|
jpackage_name);
|
return true;
|
});
|
return sparse_array;
|
}
|
|
static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) {
|
ScopedUtfChars path_utf8(env, path);
|
if (path_utf8.c_str() == nullptr) {
|
// This will throw NPE.
|
return nullptr;
|
}
|
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
std::unique_ptr<AssetDir> asset_dir =
|
assetmanager->OpenDir(path_utf8.c_str());
|
if (asset_dir == nullptr) {
|
jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str());
|
return nullptr;
|
}
|
|
const size_t file_count = asset_dir->getFileCount();
|
|
jobjectArray array = env->NewObjectArray(file_count, g_stringClass, nullptr);
|
if (array == nullptr) {
|
return nullptr;
|
}
|
|
for (size_t i = 0; i < file_count; i++) {
|
jstring java_string = env->NewStringUTF(asset_dir->getFileName(i).string());
|
|
// Check for errors creating the strings (if malformed or no memory).
|
if (env->ExceptionCheck()) {
|
return nullptr;
|
}
|
|
env->SetObjectArrayElement(array, i, java_string);
|
|
// If we have a large amount of string in our array, we might overflow the
|
// local reference table of the VM.
|
env->DeleteLocalRef(java_string);
|
}
|
return array;
|
}
|
|
static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
|
jint access_mode) {
|
ScopedUtfChars asset_path_utf8(env, asset_path);
|
if (asset_path_utf8.c_str() == nullptr) {
|
// This will throw NPE.
|
return 0;
|
}
|
|
ATRACE_NAME(base::StringPrintf("AssetManager::OpenAsset(%s)", asset_path_utf8.c_str()).c_str());
|
|
if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
|
access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
|
jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
|
return 0;
|
}
|
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
std::unique_ptr<Asset> asset =
|
assetmanager->Open(asset_path_utf8.c_str(), static_cast<Asset::AccessMode>(access_mode));
|
if (!asset) {
|
jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
|
return 0;
|
}
|
return reinterpret_cast<jlong>(asset.release());
|
}
|
|
static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
|
jlongArray out_offsets) {
|
ScopedUtfChars asset_path_utf8(env, asset_path);
|
if (asset_path_utf8.c_str() == nullptr) {
|
// This will throw NPE.
|
return nullptr;
|
}
|
|
ATRACE_NAME(base::StringPrintf("AssetManager::OpenAssetFd(%s)", asset_path_utf8.c_str()).c_str());
|
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
std::unique_ptr<Asset> asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
|
if (!asset) {
|
jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
|
return nullptr;
|
}
|
return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
|
}
|
|
static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
|
jstring asset_path, jint access_mode) {
|
ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
|
ScopedUtfChars asset_path_utf8(env, asset_path);
|
if (asset_path_utf8.c_str() == nullptr) {
|
// This will throw NPE.
|
return 0;
|
}
|
|
ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAsset(%s)", asset_path_utf8.c_str()).c_str());
|
|
if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
|
access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
|
jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
|
return 0;
|
}
|
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
std::unique_ptr<Asset> asset;
|
if (cookie != kInvalidCookie) {
|
asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie,
|
static_cast<Asset::AccessMode>(access_mode));
|
} else {
|
asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(),
|
static_cast<Asset::AccessMode>(access_mode));
|
}
|
|
if (!asset) {
|
jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
|
return 0;
|
}
|
return reinterpret_cast<jlong>(asset.release());
|
}
|
|
static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
|
jstring asset_path, jlongArray out_offsets) {
|
ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
|
ScopedUtfChars asset_path_utf8(env, asset_path);
|
if (asset_path_utf8.c_str() == nullptr) {
|
// This will throw NPE.
|
return nullptr;
|
}
|
|
ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAssetFd(%s)", asset_path_utf8.c_str()).c_str());
|
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
std::unique_ptr<Asset> asset;
|
if (cookie != kInvalidCookie) {
|
asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
|
} else {
|
asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
|
}
|
|
if (!asset) {
|
jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
|
return nullptr;
|
}
|
return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
|
}
|
|
static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie,
|
jstring asset_path) {
|
ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
|
ScopedUtfChars asset_path_utf8(env, asset_path);
|
if (asset_path_utf8.c_str() == nullptr) {
|
// This will throw NPE.
|
return 0;
|
}
|
|
ATRACE_NAME(base::StringPrintf("AssetManager::OpenXmlAsset(%s)", asset_path_utf8.c_str()).c_str());
|
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
std::unique_ptr<Asset> asset;
|
if (cookie != kInvalidCookie) {
|
asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
|
} else {
|
asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie);
|
}
|
|
if (!asset) {
|
jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
|
return 0;
|
}
|
|
// May be nullptr.
|
const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie);
|
|
std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(dynamic_ref_table);
|
status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
|
asset.reset();
|
|
if (err != NO_ERROR) {
|
jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
|
return 0;
|
}
|
return reinterpret_cast<jlong>(xml_tree.release());
|
}
|
|
static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
|
jshort density, jobject typed_value,
|
jboolean resolve_references) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
Res_value value;
|
ResTable_config selected_config;
|
uint32_t flags;
|
ApkAssetsCookie cookie =
|
assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
|
static_cast<uint16_t>(density), &value, &selected_config, &flags);
|
if (cookie == kInvalidCookie) {
|
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
|
}
|
|
uint32_t ref = static_cast<uint32_t>(resid);
|
if (resolve_references) {
|
cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
|
if (cookie == kInvalidCookie) {
|
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
|
}
|
}
|
return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value);
|
}
|
|
static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
|
jint bag_entry_id, jobject typed_value) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
|
if (bag == nullptr) {
|
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
|
}
|
|
uint32_t type_spec_flags = bag->type_spec_flags;
|
ApkAssetsCookie cookie = kInvalidCookie;
|
const Res_value* bag_value = nullptr;
|
for (const ResolvedBag::Entry& entry : bag) {
|
if (entry.key == static_cast<uint32_t>(bag_entry_id)) {
|
cookie = entry.cookie;
|
bag_value = &entry.value;
|
|
// Keep searching (the old implementation did that).
|
}
|
}
|
|
if (cookie == kInvalidCookie) {
|
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
|
}
|
|
Res_value value = *bag_value;
|
uint32_t ref = static_cast<uint32_t>(resid);
|
ResTable_config selected_config;
|
cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref);
|
if (cookie == kInvalidCookie) {
|
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
|
}
|
return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value);
|
}
|
|
static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
|
if (bag == nullptr) {
|
return nullptr;
|
}
|
|
jintArray array = env->NewIntArray(bag->entry_count);
|
if (env->ExceptionCheck()) {
|
return nullptr;
|
}
|
|
for (uint32_t i = 0; i < bag->entry_count; i++) {
|
jint attr_resid = bag->entries[i].key;
|
env->SetIntArrayRegion(array, i, 1, &attr_resid);
|
}
|
return array;
|
}
|
|
static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr,
|
jint resid) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
|
if (bag == nullptr) {
|
return nullptr;
|
}
|
|
jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr);
|
if (array == nullptr) {
|
return nullptr;
|
}
|
|
for (uint32_t i = 0; i < bag->entry_count; i++) {
|
const ResolvedBag::Entry& entry = bag->entries[i];
|
|
// Resolve any references to their final value.
|
Res_value value = entry.value;
|
ResTable_config selected_config;
|
uint32_t flags;
|
uint32_t ref;
|
ApkAssetsCookie cookie =
|
assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
|
if (cookie == kInvalidCookie) {
|
return nullptr;
|
}
|
|
if (value.dataType == Res_value::TYPE_STRING) {
|
const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie];
|
const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool();
|
|
jstring java_string = nullptr;
|
size_t str_len;
|
const char* str_utf8 = pool->string8At(value.data, &str_len);
|
if (str_utf8 != nullptr) {
|
java_string = env->NewStringUTF(str_utf8);
|
} else {
|
const char16_t* str_utf16 = pool->stringAt(value.data, &str_len);
|
java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16), str_len);
|
}
|
|
// Check for errors creating the strings (if malformed or no memory).
|
if (env->ExceptionCheck()) {
|
return nullptr;
|
}
|
|
env->SetObjectArrayElement(array, i, java_string);
|
|
// If we have a large amount of string in our array, we might overflow the
|
// local reference table of the VM.
|
env->DeleteLocalRef(java_string);
|
}
|
}
|
return array;
|
}
|
|
static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
|
jint resid) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
|
if (bag == nullptr) {
|
return nullptr;
|
}
|
|
jintArray array = env->NewIntArray(bag->entry_count * 2);
|
if (array == nullptr) {
|
return nullptr;
|
}
|
|
jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
|
if (buffer == nullptr) {
|
return nullptr;
|
}
|
|
for (size_t i = 0; i < bag->entry_count; i++) {
|
const ResolvedBag::Entry& entry = bag->entries[i];
|
Res_value value = entry.value;
|
ResTable_config selected_config;
|
uint32_t flags;
|
uint32_t ref;
|
ApkAssetsCookie cookie =
|
assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
|
if (cookie == kInvalidCookie) {
|
env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
|
return nullptr;
|
}
|
|
jint string_index = -1;
|
if (value.dataType == Res_value::TYPE_STRING) {
|
string_index = static_cast<jint>(value.data);
|
}
|
|
buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie);
|
buffer[(i * 2) + 1] = string_index;
|
}
|
env->ReleasePrimitiveArrayCritical(array, buffer, 0);
|
return array;
|
}
|
|
static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
|
if (bag == nullptr) {
|
return nullptr;
|
}
|
|
jintArray array = env->NewIntArray(bag->entry_count);
|
if (array == nullptr) {
|
return nullptr;
|
}
|
|
jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
|
if (buffer == nullptr) {
|
return nullptr;
|
}
|
|
for (size_t i = 0; i < bag->entry_count; i++) {
|
const ResolvedBag::Entry& entry = bag->entries[i];
|
Res_value value = entry.value;
|
ResTable_config selected_config;
|
uint32_t flags;
|
uint32_t ref;
|
ApkAssetsCookie cookie =
|
assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
|
if (cookie == kInvalidCookie) {
|
env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
|
return nullptr;
|
}
|
|
if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) {
|
buffer[i] = static_cast<jint>(value.data);
|
}
|
}
|
env->ReleasePrimitiveArrayCritical(array, buffer, 0);
|
return array;
|
}
|
|
static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
|
if (bag == nullptr) {
|
return -1;
|
}
|
return static_cast<jint>(bag->entry_count);
|
}
|
|
static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
|
jintArray out_data) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
|
if (bag == nullptr) {
|
return -1;
|
}
|
|
const jsize out_data_length = env->GetArrayLength(out_data);
|
if (env->ExceptionCheck()) {
|
return -1;
|
}
|
|
if (static_cast<jsize>(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) {
|
jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough");
|
return -1;
|
}
|
|
jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_data, nullptr));
|
if (buffer == nullptr) {
|
return -1;
|
}
|
|
jint* cursor = buffer;
|
for (size_t i = 0; i < bag->entry_count; i++) {
|
const ResolvedBag::Entry& entry = bag->entries[i];
|
Res_value value = entry.value;
|
ResTable_config selected_config;
|
selected_config.density = 0;
|
uint32_t flags = bag->type_spec_flags;
|
uint32_t ref = 0;
|
ApkAssetsCookie cookie =
|
assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
|
if (cookie == kInvalidCookie) {
|
env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT);
|
return -1;
|
}
|
|
// Deal with the special @null value -- it turns back to TYPE_NULL.
|
if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
|
value.dataType = Res_value::TYPE_NULL;
|
value.data = Res_value::DATA_NULL_UNDEFINED;
|
}
|
|
cursor[STYLE_TYPE] = static_cast<jint>(value.dataType);
|
cursor[STYLE_DATA] = static_cast<jint>(value.data);
|
cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
|
cursor[STYLE_RESOURCE_ID] = static_cast<jint>(ref);
|
cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(flags);
|
cursor[STYLE_DENSITY] = static_cast<jint>(selected_config.density);
|
cursor += STYLE_NUM_ENTRIES;
|
}
|
env->ReleasePrimitiveArrayCritical(out_data, buffer, 0);
|
return static_cast<jint>(bag->entry_count);
|
}
|
|
static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name,
|
jstring def_type, jstring def_package) {
|
ScopedUtfChars name_utf8(env, name);
|
if (name_utf8.c_str() == nullptr) {
|
// This will throw NPE.
|
return 0;
|
}
|
|
std::string type;
|
if (def_type != nullptr) {
|
ScopedUtfChars type_utf8(env, def_type);
|
CHECK(type_utf8.c_str() != nullptr);
|
type = type_utf8.c_str();
|
}
|
|
std::string package;
|
if (def_package != nullptr) {
|
ScopedUtfChars package_utf8(env, def_package);
|
CHECK(package_utf8.c_str() != nullptr);
|
package = package_utf8.c_str();
|
}
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
return static_cast<jint>(assetmanager->GetResourceId(name_utf8.c_str(), type, package));
|
}
|
|
static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
AssetManager2::ResourceName name;
|
if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
|
return nullptr;
|
}
|
|
std::string result = ToFormattedResourceString(&name);
|
return env->NewStringUTF(result.c_str());
|
}
|
|
static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
AssetManager2::ResourceName name;
|
if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
|
return nullptr;
|
}
|
|
if (name.package != nullptr) {
|
return env->NewStringUTF(name.package);
|
}
|
return nullptr;
|
}
|
|
static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
AssetManager2::ResourceName name;
|
if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
|
return nullptr;
|
}
|
|
if (name.type != nullptr) {
|
return env->NewStringUTF(name.type);
|
} else if (name.type16 != nullptr) {
|
return env->NewString(reinterpret_cast<const jchar*>(name.type16), name.type_len);
|
}
|
return nullptr;
|
}
|
|
static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
AssetManager2::ResourceName name;
|
if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
|
return nullptr;
|
}
|
|
if (name.entry != nullptr) {
|
return env->NewStringUTF(name.entry);
|
} else if (name.entry16 != nullptr) {
|
return env->NewString(reinterpret_cast<const jchar*>(name.entry16), name.entry_len);
|
}
|
return nullptr;
|
}
|
|
static void NativeSetResourceResolutionLoggingEnabled(JNIEnv* /*env*/,
|
jclass /*clazz*/,
|
jlong ptr,
|
jboolean enabled) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
assetmanager->SetResourceResolutionLoggingEnabled(enabled);
|
}
|
|
static jstring NativeGetLastResourceResolution(JNIEnv* env,
|
jclass /*clazz*/,
|
jlong ptr) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
std::string resolution = assetmanager->GetLastResourceResolution();
|
if (resolution.empty()) {
|
return nullptr;
|
} else {
|
return env->NewStringUTF(resolution.c_str());
|
}
|
}
|
|
static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr,
|
jboolean exclude_system) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
std::set<std::string> locales =
|
assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/);
|
|
jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr);
|
if (array == nullptr) {
|
return nullptr;
|
}
|
|
size_t idx = 0;
|
for (const std::string& locale : locales) {
|
jstring java_string = env->NewStringUTF(locale.c_str());
|
if (java_string == nullptr) {
|
return nullptr;
|
}
|
env->SetObjectArrayElement(array, idx++, java_string);
|
env->DeleteLocalRef(java_string);
|
}
|
return array;
|
}
|
|
static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
|
jobject result =
|
env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor);
|
if (result == nullptr) {
|
return nullptr;
|
}
|
|
env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset,
|
config.smallestScreenWidthDp);
|
env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
|
env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
|
return result;
|
}
|
|
static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
std::set<ResTable_config> configurations =
|
assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/);
|
|
jobjectArray array =
|
env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr);
|
if (array == nullptr) {
|
return nullptr;
|
}
|
|
size_t idx = 0;
|
for (const ResTable_config& configuration : configurations) {
|
jobject java_configuration = ConstructConfigurationObject(env, configuration);
|
if (java_configuration == nullptr) {
|
return nullptr;
|
}
|
|
env->SetObjectArrayElement(array, idx++, java_configuration);
|
env->DeleteLocalRef(java_configuration);
|
}
|
return array;
|
}
|
|
static jintArray NativeAttributeResolutionStack(
|
JNIEnv* env, jclass /*clazz*/, jlong ptr,
|
jlong theme_ptr, jint xml_style_res,
|
jint def_style_attr, jint def_style_resid) {
|
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
|
CHECK(theme->GetAssetManager() == &(*assetmanager));
|
(void) assetmanager;
|
|
// Load default style from attribute, if specified...
|
uint32_t def_style_flags = 0u;
|
if (def_style_attr != 0) {
|
Res_value value;
|
if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
|
if (value.dataType == Res_value::TYPE_REFERENCE) {
|
def_style_resid = value.data;
|
}
|
}
|
}
|
|
auto style_stack = assetmanager->GetBagResIdStack(xml_style_res);
|
auto def_style_stack = assetmanager->GetBagResIdStack(def_style_resid);
|
|
jintArray array = env->NewIntArray(style_stack.size() + def_style_stack.size());
|
if (env->ExceptionCheck()) {
|
return nullptr;
|
}
|
|
for (uint32_t i = 0; i < style_stack.size(); i++) {
|
jint attr_resid = style_stack[i];
|
env->SetIntArrayRegion(array, i, 1, &attr_resid);
|
}
|
for (uint32_t i = 0; i < def_style_stack.size(); i++) {
|
jint attr_resid = def_style_stack[i];
|
env->SetIntArrayRegion(array, style_stack.size() + i, 1, &attr_resid);
|
}
|
return array;
|
}
|
|
static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
|
jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr,
|
jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
|
CHECK(theme->GetAssetManager() == &(*assetmanager));
|
(void) assetmanager;
|
|
ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
|
uint32_t* out_values = reinterpret_cast<uint32_t*>(out_values_ptr);
|
uint32_t* out_indices = reinterpret_cast<uint32_t*>(out_indices_ptr);
|
|
jsize attrs_len = env->GetArrayLength(java_attrs);
|
jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
|
if (attrs == nullptr) {
|
return;
|
}
|
|
ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr),
|
static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(attrs), attrs_len,
|
out_values, out_indices);
|
env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
|
}
|
|
static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
|
jint def_style_attr, jint def_style_resid, jintArray java_values,
|
jintArray java_attrs, jintArray out_java_values,
|
jintArray out_java_indices) {
|
const jsize attrs_len = env->GetArrayLength(java_attrs);
|
const jsize out_values_len = env->GetArrayLength(out_java_values);
|
if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
|
jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
|
return JNI_FALSE;
|
}
|
|
jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
|
if (attrs == nullptr) {
|
return JNI_FALSE;
|
}
|
|
jint* values = nullptr;
|
jsize values_len = 0;
|
if (java_values != nullptr) {
|
values_len = env->GetArrayLength(java_values);
|
values = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_values, nullptr));
|
if (values == nullptr) {
|
env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
|
return JNI_FALSE;
|
}
|
}
|
|
jint* out_values =
|
reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
|
if (out_values == nullptr) {
|
env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
|
if (values != nullptr) {
|
env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
|
}
|
return JNI_FALSE;
|
}
|
|
jint* out_indices = nullptr;
|
if (out_java_indices != nullptr) {
|
jsize out_indices_len = env->GetArrayLength(out_java_indices);
|
if (out_indices_len > attrs_len) {
|
out_indices =
|
reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
|
if (out_indices == nullptr) {
|
env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
|
if (values != nullptr) {
|
env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
|
}
|
env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
|
return JNI_FALSE;
|
}
|
}
|
}
|
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
|
CHECK(theme->GetAssetManager() == &(*assetmanager));
|
(void) assetmanager;
|
|
bool result = ResolveAttrs(
|
theme, static_cast<uint32_t>(def_style_attr), static_cast<uint32_t>(def_style_resid),
|
reinterpret_cast<uint32_t*>(values), values_len, reinterpret_cast<uint32_t*>(attrs),
|
attrs_len, reinterpret_cast<uint32_t*>(out_values), reinterpret_cast<uint32_t*>(out_indices));
|
if (out_indices != nullptr) {
|
env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
|
}
|
|
env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
|
if (values != nullptr) {
|
env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
|
}
|
env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
|
return result ? JNI_TRUE : JNI_FALSE;
|
}
|
|
static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr,
|
jlong xml_parser_ptr, jintArray java_attrs,
|
jintArray out_java_values, jintArray out_java_indices) {
|
const jsize attrs_len = env->GetArrayLength(java_attrs);
|
const jsize out_values_len = env->GetArrayLength(out_java_values);
|
if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
|
jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
|
return JNI_FALSE;
|
}
|
|
jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
|
if (attrs == nullptr) {
|
return JNI_FALSE;
|
}
|
|
jint* out_values =
|
reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
|
if (out_values == nullptr) {
|
env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
|
return JNI_FALSE;
|
}
|
|
jint* out_indices = nullptr;
|
if (out_java_indices != nullptr) {
|
jsize out_indices_len = env->GetArrayLength(out_java_indices);
|
if (out_indices_len > attrs_len) {
|
out_indices =
|
reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
|
if (out_indices == nullptr) {
|
env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
|
env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
|
return JNI_FALSE;
|
}
|
}
|
}
|
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
|
|
bool result = RetrieveAttributes(assetmanager.get(), xml_parser,
|
reinterpret_cast<uint32_t*>(attrs), attrs_len,
|
reinterpret_cast<uint32_t*>(out_values),
|
reinterpret_cast<uint32_t*>(out_indices));
|
|
if (out_indices != nullptr) {
|
env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
|
}
|
env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
|
env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
|
return result ? JNI_TRUE : JNI_FALSE;
|
}
|
|
static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
return reinterpret_cast<jlong>(assetmanager->NewTheme().release());
|
}
|
|
static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
|
delete reinterpret_cast<Theme*>(theme_ptr);
|
}
|
|
static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
|
jint resid, jboolean force) {
|
// AssetManager is accessed via the theme, so grab an explicit lock here.
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
|
CHECK(theme->GetAssetManager() == &(*assetmanager));
|
(void) assetmanager;
|
theme->ApplyStyle(static_cast<uint32_t>(resid), force);
|
|
// TODO(adamlesinski): Consider surfacing exception when result is failure.
|
// CTS currently expects no exceptions from this method.
|
// std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid);
|
// jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
|
}
|
|
static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_asset_manager_ptr,
|
jlong dst_theme_ptr, jlong src_asset_manager_ptr, jlong src_theme_ptr) {
|
Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
|
Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr);
|
|
if (dst_asset_manager_ptr != src_asset_manager_ptr) {
|
ScopedLock<AssetManager2> dst_assetmanager(AssetManagerFromLong(dst_asset_manager_ptr));
|
CHECK(dst_theme->GetAssetManager() == &(*dst_assetmanager));
|
(void) dst_assetmanager;
|
|
ScopedLock <AssetManager2> src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr));
|
CHECK(src_theme->GetAssetManager() == &(*src_assetmanager));
|
(void) src_assetmanager;
|
|
dst_theme->SetTo(*src_theme);
|
} else {
|
dst_theme->SetTo(*src_theme);
|
}
|
}
|
|
static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
|
reinterpret_cast<Theme*>(theme_ptr)->Clear();
|
}
|
|
static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
|
jint resid, jobject typed_value,
|
jboolean resolve_references) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
|
CHECK(theme->GetAssetManager() == &(*assetmanager));
|
(void) assetmanager;
|
|
Res_value value;
|
uint32_t flags;
|
ApkAssetsCookie cookie = theme->GetAttribute(static_cast<uint32_t>(resid), &value, &flags);
|
if (cookie == kInvalidCookie) {
|
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
|
}
|
|
uint32_t ref = 0u;
|
if (resolve_references) {
|
ResTable_config selected_config;
|
cookie =
|
theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
|
if (cookie == kInvalidCookie) {
|
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
|
}
|
}
|
return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value);
|
}
|
|
static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
|
jint priority, jstring tag, jstring prefix) {
|
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
|
Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
|
CHECK(theme->GetAssetManager() == &(*assetmanager));
|
(void) assetmanager;
|
(void) priority;
|
(void) tag;
|
(void) prefix;
|
|
theme->Dump();
|
}
|
|
static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/,
|
jlong theme_ptr) {
|
Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
|
return static_cast<jint>(theme->GetChangingConfigurations());
|
}
|
|
static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
|
delete reinterpret_cast<Asset*>(asset_ptr);
|
}
|
|
static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
|
Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
|
uint8_t b;
|
ssize_t res = asset->read(&b, sizeof(b));
|
return res == sizeof(b) ? static_cast<jint>(b) : -1;
|
}
|
|
static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer,
|
jint offset, jint len) {
|
if (len == 0) {
|
return 0;
|
}
|
|
jsize buffer_len = env->GetArrayLength(java_buffer);
|
if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len ||
|
offset > buffer_len - len) {
|
jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
|
return -1;
|
}
|
|
ScopedByteArrayRW byte_array(env, java_buffer);
|
if (byte_array.get() == nullptr) {
|
return -1;
|
}
|
|
Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
|
ssize_t res = asset->read(byte_array.get() + offset, len);
|
if (res < 0) {
|
jniThrowException(env, "java/io/IOException", "");
|
return -1;
|
}
|
return res > 0 ? static_cast<jint>(res) : -1;
|
}
|
|
static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset,
|
jint whence) {
|
Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
|
return static_cast<jlong>(asset->seek(
|
static_cast<off64_t>(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR))));
|
}
|
|
static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
|
Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
|
return static_cast<jlong>(asset->getLength());
|
}
|
|
static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
|
Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
|
return static_cast<jlong>(asset->getRemainingLength());
|
}
|
|
// ----------------------------------------------------------------------------
|
|
// JNI registration.
|
static const JNINativeMethod gAssetManagerMethods[] = {
|
// AssetManager setup methods.
|
{"nativeCreate", "()J", (void*)NativeCreate},
|
{"nativeDestroy", "(J)V", (void*)NativeDestroy},
|
{"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
|
{"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V",
|
(void*)NativeSetConfiguration},
|
{"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;",
|
(void*)NativeGetAssignedPackageIdentifiers},
|
|
// AssetManager file methods.
|
{"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
|
{"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
|
{"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
|
(void*)NativeOpenAssetFd},
|
{"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset},
|
{"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
|
(void*)NativeOpenNonAssetFd},
|
{"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
|
|
// AssetManager resource methods.
|
{"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue},
|
{"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I",
|
(void*)NativeGetResourceBagValue},
|
{"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes},
|
{"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;",
|
(void*)NativeGetResourceStringArray},
|
{"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo},
|
{"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
|
{"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
|
{"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
|
|
// AssetManager resource name/ID methods.
|
{"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
|
(void*)NativeGetResourceIdentifier},
|
{"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName},
|
{"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName},
|
{"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
|
{"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
|
{"nativeSetResourceResolutionLoggingEnabled", "(JZ)V",
|
(void*) NativeSetResourceResolutionLoggingEnabled},
|
{"nativeGetLastResourceResolution", "(J)Ljava/lang/String;",
|
(void*) NativeGetLastResourceResolution},
|
{"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
|
{"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
|
(void*)NativeGetSizeConfigurations},
|
|
// Style attribute related methods.
|
{"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack},
|
{"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
|
{"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
|
{"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
|
|
// Theme related methods.
|
{"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
|
{"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy},
|
{"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
|
{"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy},
|
{"nativeThemeClear", "(J)V", (void*)NativeThemeClear},
|
{"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
|
(void*)NativeThemeGetAttributeValue},
|
{"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
|
{"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations},
|
|
// AssetInputStream methods.
|
{"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy},
|
{"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar},
|
{"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead},
|
{"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek},
|
{"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength},
|
{"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
|
|
// System/idmap related methods.
|
{"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps},
|
{"nativeCreateIdmapsForStaticOverlaysTargetingAndroid", "()[Ljava/lang/String;",
|
(void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid},
|
{"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
|
(void*)NativeGetOverlayableMap},
|
|
// Global management/debug methods.
|
{"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
|
{"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations},
|
{"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount},
|
};
|
|
int register_android_content_AssetManager(JNIEnv* env) {
|
jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets");
|
gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J");
|
|
jclass typedValue = FindClassOrDie(env, "android/util/TypedValue");
|
gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I");
|
gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I");
|
gTypedValueOffsets.mString =
|
GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;");
|
gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I");
|
gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I");
|
gTypedValueOffsets.mChangingConfigurations =
|
GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I");
|
gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
|
|
jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
|
gAssetFileDescriptorOffsets.mFd =
|
GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
|
gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
|
gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
|
|
jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
|
gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
|
|
jclass stringClass = FindClassOrDie(env, "java/lang/String");
|
g_stringClass = MakeGlobalRefOrDie(env, stringClass);
|
|
jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
|
gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
|
gSparseArrayOffsets.constructor =
|
GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "<init>", "()V");
|
gSparseArrayOffsets.put =
|
GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V");
|
|
jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
|
gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
|
gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "<init>", "()V");
|
gConfigurationOffsets.mSmallestScreenWidthDpOffset =
|
GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I");
|
gConfigurationOffsets.mScreenWidthDpOffset =
|
GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I");
|
gConfigurationOffsets.mScreenHeightDpOffset =
|
GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I");
|
|
jclass arrayMapClass = FindClassOrDie(env, "android/util/ArrayMap");
|
gArrayMapOffsets.classObject = MakeGlobalRefOrDie(env, arrayMapClass);
|
gArrayMapOffsets.constructor =
|
GetMethodIDOrDie(env, gArrayMapOffsets.classObject, "<init>", "()V");
|
gArrayMapOffsets.put =
|
GetMethodIDOrDie(env, gArrayMapOffsets.classObject, "put",
|
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
|
return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
|
NELEM(gAssetManagerMethods));
|
}
|
|
}; // namespace android
|