blob: bf56ef438a1d14203991428fa588bf939aa5fbfd [file] [log] [blame]
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001/*
2 * Copyright 2006, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080016
Adam Lesinskibebfcc42018-02-12 14:27:46 -080017#define ATRACE_TAG ATRACE_TAG_RESOURCES
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018#define LOG_TAG "asset"
19
Dan Albert46d84442014-11-18 16:07:51 -080020#include <inttypes.h>
21#include <linux/capability.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022#include <stdio.h>
Adam Lesinskibde1df22018-02-09 11:12:22 -080023#include <sys/stat.h>
24#include <sys/system_properties.h>
Adam Lesinskibebfcc42018-02-12 14:27:46 -080025#include <sys/types.h>
26#include <sys/wait.h>
Mårten Kongstad06a1ac82018-09-20 13:09:47 +020027#include <unistd.h>
Mårten Kongstad48d22322014-01-31 14:43:27 +010028
Dan Albert46d84442014-11-18 16:07:51 -080029#include <private/android_filesystem_config.h> // for AID_SYSTEM
30
Mårten Kongstad06a1ac82018-09-20 13:09:47 +020031#include <sstream>
32#include <string>
33
Adam Lesinskibebfcc42018-02-12 14:27:46 -080034#include "android-base/logging.h"
35#include "android-base/properties.h"
36#include "android-base/stringprintf.h"
37#include "android_runtime/android_util_AssetManager.h"
Adam Lesinskibde1df22018-02-09 11:12:22 -080038#include "android_runtime/AndroidRuntime.h"
39#include "android_util_Binder.h"
Adam Lesinskibebfcc42018-02-12 14:27:46 -080040#include "androidfw/Asset.h"
41#include "androidfw/AssetManager.h"
42#include "androidfw/AssetManager2.h"
43#include "androidfw/AttributeResolution.h"
44#include "androidfw/MutexGuard.h"
Mårten Kongstad06a1ac82018-09-20 13:09:47 +020045#include "androidfw/PosixUtils.h"
Adam Lesinskibebfcc42018-02-12 14:27:46 -080046#include "androidfw/ResourceTypes.h"
Winson2f3669b2019-01-11 11:28:34 -080047#include "androidfw/ResourceUtils.h"
48
Dan Albert3a091b72014-11-20 15:41:25 -080049#include "core_jni_helpers.h"
50#include "jni.h"
Adam Lesinskibebfcc42018-02-12 14:27:46 -080051#include "nativehelper/JNIHelp.h"
52#include "nativehelper/ScopedPrimitiveArray.h"
53#include "nativehelper/ScopedStringChars.h"
54#include "nativehelper/ScopedUtfChars.h"
Dan Albert46d84442014-11-18 16:07:51 -080055#include "utils/Log.h"
Adam Lesinski11875902017-01-23 12:58:11 -080056#include "utils/misc.h"
Adam Lesinskibde1df22018-02-09 11:12:22 -080057#include "utils/String8.h"
Adam Lesinskibebfcc42018-02-12 14:27:46 -080058#include "utils/Trace.h"
Dan Albert46d84442014-11-18 16:07:51 -080059
Mårten Kongstad48d22322014-01-31 14:43:27 +010060extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
61extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
62
Adam Lesinskibebfcc42018-02-12 14:27:46 -080063using ::android::base::StringPrintf;
Mårten Kongstad06a1ac82018-09-20 13:09:47 +020064using ::android::util::ExecuteBinary;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065
66namespace android {
67
68// ----------------------------------------------------------------------------
69
Adam Lesinskibebfcc42018-02-12 14:27:46 -080070static struct typedvalue_offsets_t {
71 jfieldID mType;
72 jfieldID mData;
73 jfieldID mString;
74 jfieldID mAssetCookie;
75 jfieldID mResourceId;
76 jfieldID mChangingConfigurations;
77 jfieldID mDensity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078} gTypedValueOffsets;
79
Adam Lesinskibebfcc42018-02-12 14:27:46 -080080static struct assetfiledescriptor_offsets_t {
81 jfieldID mFd;
82 jfieldID mStartOffset;
83 jfieldID mLength;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084} gAssetFileDescriptorOffsets;
85
Nicolas Geoffraya55a59a2018-05-10 15:00:13 +010086// This is also used by asset_manager.cpp.
87assetmanager_offsets_t gAssetManagerOffsets;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088
Adam Lesinskibebfcc42018-02-12 14:27:46 -080089static struct {
90 jfieldID native_ptr;
91} gApkAssetsFields;
92
93static struct sparsearray_offsets_t {
94 jclass classObject;
95 jmethodID constructor;
96 jmethodID put;
Adam Lesinskide898ff2014-01-29 18:20:45 -080097} gSparseArrayOffsets;
98
Adam Lesinskibebfcc42018-02-12 14:27:46 -080099static struct configuration_offsets_t {
100 jclass classObject;
101 jmethodID constructor;
102 jfieldID mSmallestScreenWidthDpOffset;
103 jfieldID mScreenWidthDpOffset;
104 jfieldID mScreenHeightDpOffset;
Filip Gruszczynski23493322015-07-29 17:02:59 -0700105} gConfigurationOffsets;
106
Mårten Kongstadc92c4dd2019-02-05 01:29:59 +0100107static struct arraymap_offsets_t {
108 jclass classObject;
109 jmethodID constructor;
110 jmethodID put;
111} gArrayMapOffsets;
112
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800113jclass g_stringClass = nullptr;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114
115// ----------------------------------------------------------------------------
116
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800117// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
118constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
119 return cookie != kInvalidCookie ? static_cast<jint>(cookie + 1) : -1;
120}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800122constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) {
123 return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124}
125
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900126// This is called by zygote (running as user root) as part of preloadResources.
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800127static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) {
128 switch (pid_t pid = fork()) {
129 case -1:
130 PLOG(ERROR) << "failed to fork for idmap";
131 break;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900132
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800133 // child
134 case 0: {
135 struct __user_cap_header_struct capheader;
136 struct __user_cap_data_struct capdata;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900137
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800138 memset(&capheader, 0, sizeof(capheader));
139 memset(&capdata, 0, sizeof(capdata));
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900140
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800141 capheader.version = _LINUX_CAPABILITY_VERSION;
142 capheader.pid = 0;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900143
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800144 if (capget(&capheader, &capdata) != 0) {
145 PLOG(ERROR) << "capget";
146 exit(1);
147 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900148
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800149 capdata.effective = capdata.permitted;
150 if (capset(&capheader, &capdata) != 0) {
151 PLOG(ERROR) << "capset";
152 exit(1);
153 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900154
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800155 if (setgid(AID_SYSTEM) != 0) {
156 PLOG(ERROR) << "setgid";
157 exit(1);
158 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900159
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800160 if (setuid(AID_SYSTEM) != 0) {
161 PLOG(ERROR) << "setuid";
162 exit(1);
163 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900164
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800165 // Generic idmap parameters
166 const char* argv[8];
167 int argc = 0;
168 struct stat st;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900169
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800170 memset(argv, 0, sizeof(argv));
171 argv[argc++] = AssetManager::IDMAP_BIN;
172 argv[argc++] = "--scan";
173 argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
174 argv[argc++] = AssetManager::TARGET_APK_PATH;
175 argv[argc++] = AssetManager::IDMAP_DIR;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900176
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800177 // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
Mårten Kongstad06a1ac82018-09-20 13:09:47 +0200178 // use VENDOR_OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in
179 // addition to VENDOR_OVERLAY_DIR.
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800180 std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY,
181 "");
182 if (!overlay_theme_path.empty()) {
Mårten Kongstad06a1ac82018-09-20 13:09:47 +0200183 overlay_theme_path =
184 std::string(AssetManager::VENDOR_OVERLAY_DIR) + "/" + overlay_theme_path;
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800185 if (stat(overlay_theme_path.c_str(), &st) == 0) {
186 argv[argc++] = overlay_theme_path.c_str();
Adam Lesinskiec7f06c2018-02-09 11:01:26 -0800187 }
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800188 }
Adam Lesinski11875902017-01-23 12:58:11 -0800189
Mårten Kongstad06a1ac82018-09-20 13:09:47 +0200190 if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
191 argv[argc++] = AssetManager::VENDOR_OVERLAY_DIR;
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800192 }
Adam Lesinski11875902017-01-23 12:58:11 -0800193
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800194 if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
195 argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR;
196 }
197
Dario Freni4ce46792018-06-01 14:02:08 +0100198 if (stat(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR, &st) == 0) {
199 argv[argc++] = AssetManager::PRODUCT_SERVICES_OVERLAY_DIR;
200 }
201
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800202 // Finally, invoke idmap (if any overlay directory exists)
203 if (argc > 5) {
204 execv(AssetManager::IDMAP_BIN, (char* const*)argv);
205 PLOG(ERROR) << "failed to execv for idmap";
206 exit(1); // should never get here
207 } else {
208 exit(0);
209 }
210 } break;
211
212 // parent
213 default:
214 waitpid(pid, nullptr, 0);
215 break;
216 }
Adam Lesinski11875902017-01-23 12:58:11 -0800217}
218
Mårten Kongstad06a1ac82018-09-20 13:09:47 +0200219static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* env,
220 jclass /*clazz*/) {
221 // --input-directory can be given multiple times, but idmap2 expects the directory to exist
222 std::vector<std::string> input_dirs;
223 struct stat st;
224 if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
225 input_dirs.push_back(AssetManager::VENDOR_OVERLAY_DIR);
226 }
227
228 if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
229 input_dirs.push_back(AssetManager::PRODUCT_OVERLAY_DIR);
230 }
231
232 if (stat(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR, &st) == 0) {
233 input_dirs.push_back(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR);
234 }
235
236 if (input_dirs.empty()) {
237 LOG(WARNING) << "no directories for idmap2 to scan";
238 return env->NewObjectArray(0, g_stringClass, nullptr);
239 }
240
Yabin Cuia60dc9e2019-02-26 16:12:46 -0800241 if (access("/system/bin/idmap2", X_OK) == -1) {
242 PLOG(WARNING) << "unable to execute idmap2";
243 return nullptr;
244 }
245
Mårten Kongstad06a1ac82018-09-20 13:09:47 +0200246 std::vector<std::string> argv{"/system/bin/idmap2",
247 "scan",
248 "--recursive",
249 "--target-package-name", "android",
250 "--target-apk-path", "/system/framework/framework-res.apk",
251 "--output-directory", "/data/resource-cache"};
252
253 for (const auto& dir : input_dirs) {
254 argv.push_back("--input-directory");
255 argv.push_back(dir);
256 }
257
258 const auto result = ExecuteBinary(argv);
259
260 if (!result) {
261 LOG(ERROR) << "failed to execute idmap2";
262 return nullptr;
263 }
264
265 if (result->status != 0) {
266 LOG(ERROR) << "idmap2: " << result->stderr;
267 return nullptr;
268 }
269
270 std::vector<std::string> idmap_paths;
271 std::istringstream input(result->stdout);
272 std::string path;
273 while (std::getline(input, path)) {
274 idmap_paths.push_back(path);
275 }
276
277 jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr);
278 if (array == nullptr) {
279 return nullptr;
280 }
281 for (size_t i = 0; i < idmap_paths.size(); i++) {
282 const std::string path = idmap_paths[i];
283 jstring java_string = env->NewStringUTF(path.c_str());
284 if (env->ExceptionCheck()) {
285 return nullptr;
286 }
287 env->SetObjectArrayElement(array, i, java_string);
288 env->DeleteLocalRef(java_string);
289 }
290 return array;
291}
292
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800293static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
294 uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
295 env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
296 env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie,
297 ApkAssetsCookieToJavaCookie(cookie));
298 env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data);
299 env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr);
300 env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref);
301 env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags);
302 if (config != nullptr) {
303 env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density);
304 }
305 return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie));
Adam Lesinski11875902017-01-23 12:58:11 -0800306}
307
308// ----------------------------------------------------------------------------
309
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800310// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance.
311struct GuardedAssetManager : public ::AAssetManager {
312 Guarded<AssetManager2> guarded_assetmanager;
Adam Lesinski11875902017-01-23 12:58:11 -0800313};
314
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800315::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
316 jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject);
317 ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle);
318 if (am == nullptr) {
319 jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
320 return nullptr;
321 }
322 return am;
323}
Adam Lesinski11875902017-01-23 12:58:11 -0800324
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800325Guarded<AssetManager2>* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) {
326 if (assetmanager == nullptr) {
327 return nullptr;
328 }
329 return &reinterpret_cast<GuardedAssetManager*>(assetmanager)->guarded_assetmanager;
330}
Adam Lesinski11875902017-01-23 12:58:11 -0800331
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800332Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
333 return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager));
334}
Adam Lesinski11875902017-01-23 12:58:11 -0800335
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800336static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) {
337 return *AssetManagerForNdkAssetManager(reinterpret_cast<AAssetManager*>(ptr));
338}
Adam Lesinski11875902017-01-23 12:58:11 -0800339
Mårten Kongstadc92c4dd2019-02-05 01:29:59 +0100340static jobject NativeGetOverlayableMap(JNIEnv* env, jclass /*clazz*/, jlong ptr,
341 jstring package_name) {
342 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
343 const ScopedUtfChars package_name_utf8(env, package_name);
344 CHECK(package_name_utf8.c_str() != nullptr);
345 const std::string std_package_name(package_name_utf8.c_str());
346 const std::unordered_map<std::string, std::string>* map = nullptr;
347
348 assetmanager->ForEachPackage([&](const std::string& this_package_name, uint8_t package_id) {
349 if (this_package_name == std_package_name) {
350 map = assetmanager->GetOverlayableMapForPackage(package_id);
Ryan Mitchella55dc2e2019-01-24 10:58:23 -0800351 return false;
Mårten Kongstadc92c4dd2019-02-05 01:29:59 +0100352 }
Ryan Mitchella55dc2e2019-01-24 10:58:23 -0800353 return true;
Mårten Kongstadc92c4dd2019-02-05 01:29:59 +0100354 });
355
356 if (map == nullptr) {
357 return nullptr;
358 }
359
360 jobject array_map = env->NewObject(gArrayMapOffsets.classObject, gArrayMapOffsets.constructor);
361 if (array_map == nullptr) {
362 return nullptr;
363 }
364
365 for (const auto& iter : *map) {
366 jstring name = env->NewStringUTF(iter.first.c_str());
367 if (env->ExceptionCheck()) {
368 return nullptr;
369 }
370
371 jstring actor = env->NewStringUTF(iter.second.c_str());
372 if (env->ExceptionCheck()) {
373 env->DeleteLocalRef(name);
374 return nullptr;
375 }
376
377 env->CallObjectMethod(array_map, gArrayMapOffsets.put, name, actor);
378
379 env->DeleteLocalRef(name);
380 env->DeleteLocalRef(actor);
381 }
382
383 return array_map;
384}
385
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800386static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
387 jlongArray out_offsets) {
388 off64_t start_offset, length;
389 int fd = asset->openFileDescriptor(&start_offset, &length);
390 asset.reset();
Adam Lesinski11875902017-01-23 12:58:11 -0800391
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800392 if (fd < 0) {
393 jniThrowException(env, "java/io/FileNotFoundException",
394 "This file can not be opened as a file descriptor; it is probably "
395 "compressed");
396 return nullptr;
397 }
Adam Lesinski11875902017-01-23 12:58:11 -0800398
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800399 jlong* offsets = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(out_offsets, 0));
400 if (offsets == nullptr) {
401 close(fd);
402 return nullptr;
403 }
404
405 offsets[0] = start_offset;
406 offsets[1] = length;
407
408 env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0);
409
410 jobject file_desc = jniCreateFileDescriptor(env, fd);
411 if (file_desc == nullptr) {
412 close(fd);
413 return nullptr;
414 }
415 return newParcelFileDescriptor(env, file_desc);
416}
417
418static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) {
419 return Asset::getGlobalCount();
420}
421
422static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) {
423 String8 alloc = Asset::getAssetAllocations();
424 if (alloc.length() <= 0) {
425 return nullptr;
426 }
427 return env->NewStringUTF(alloc.string());
428}
429
430static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) {
431 // TODO(adamlesinski): Switch to AssetManager2.
432 return AssetManager::getGlobalCount();
433}
434
435static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) {
436 // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and
437 // AssetManager2 in a contiguous block (GuardedAssetManager).
438 return reinterpret_cast<jlong>(new GuardedAssetManager());
439}
440
441static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
442 delete reinterpret_cast<GuardedAssetManager*>(ptr);
443}
444
445static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
446 jobjectArray apk_assets_array, jboolean invalidate_caches) {
447 ATRACE_NAME("AssetManager::SetApkAssets");
448
449 const jsize apk_assets_len = env->GetArrayLength(apk_assets_array);
450 std::vector<const ApkAssets*> apk_assets;
451 apk_assets.reserve(apk_assets_len);
452 for (jsize i = 0; i < apk_assets_len; i++) {
453 jobject obj = env->GetObjectArrayElement(apk_assets_array, i);
454 if (obj == nullptr) {
455 std::string msg = StringPrintf("ApkAssets at index %d is null", i);
456 jniThrowNullPointerException(env, msg.c_str());
457 return;
458 }
459
460 jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr);
461 if (env->ExceptionCheck()) {
462 return;
463 }
464 apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr));
465 }
466
467 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
468 assetmanager->SetApkAssets(apk_assets, invalidate_caches);
469}
470
471static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
472 jstring locale, jint orientation, jint touchscreen, jint density,
473 jint keyboard, jint keyboard_hidden, jint navigation,
474 jint screen_width, jint screen_height,
475 jint smallest_screen_width_dp, jint screen_width_dp,
476 jint screen_height_dp, jint screen_layout, jint ui_mode,
477 jint color_mode, jint major_version) {
478 ATRACE_NAME("AssetManager::SetConfiguration");
479
480 ResTable_config configuration;
481 memset(&configuration, 0, sizeof(configuration));
482 configuration.mcc = static_cast<uint16_t>(mcc);
483 configuration.mnc = static_cast<uint16_t>(mnc);
484 configuration.orientation = static_cast<uint8_t>(orientation);
485 configuration.touchscreen = static_cast<uint8_t>(touchscreen);
486 configuration.density = static_cast<uint16_t>(density);
487 configuration.keyboard = static_cast<uint8_t>(keyboard);
488 configuration.inputFlags = static_cast<uint8_t>(keyboard_hidden);
489 configuration.navigation = static_cast<uint8_t>(navigation);
490 configuration.screenWidth = static_cast<uint16_t>(screen_width);
491 configuration.screenHeight = static_cast<uint16_t>(screen_height);
492 configuration.smallestScreenWidthDp = static_cast<uint16_t>(smallest_screen_width_dp);
493 configuration.screenWidthDp = static_cast<uint16_t>(screen_width_dp);
494 configuration.screenHeightDp = static_cast<uint16_t>(screen_height_dp);
495 configuration.screenLayout = static_cast<uint8_t>(screen_layout);
496 configuration.uiMode = static_cast<uint8_t>(ui_mode);
497 configuration.colorMode = static_cast<uint8_t>(color_mode);
498 configuration.sdkVersion = static_cast<uint16_t>(major_version);
499
500 if (locale != nullptr) {
501 ScopedUtfChars locale_utf8(env, locale);
502 CHECK(locale_utf8.c_str() != nullptr);
503 configuration.setBcp47Locale(locale_utf8.c_str());
504 }
505
506 // Constants duplicated from Java class android.content.res.Configuration.
507 static const jint kScreenLayoutRoundMask = 0x300;
508 static const jint kScreenLayoutRoundShift = 8;
509
510 // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
511 // in C++. We must extract the round qualifier out of the Java screenLayout and put it
512 // into screenLayout2.
513 configuration.screenLayout2 =
514 static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
515
516 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
517 assetmanager->SetConfiguration(configuration);
518}
519
520static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
521 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
522
523 jobject sparse_array =
524 env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor);
525
526 if (sparse_array == nullptr) {
527 // An exception is pending.
528 return nullptr;
529 }
530
Ryan Mitchella55dc2e2019-01-24 10:58:23 -0800531 assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) -> bool {
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800532 jstring jpackage_name = env->NewStringUTF(package_name.c_str());
533 if (jpackage_name == nullptr) {
534 // An exception is pending.
Ryan Mitchella55dc2e2019-01-24 10:58:23 -0800535 return false;
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800536 }
537
538 env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id),
539 jpackage_name);
Ryan Mitchella55dc2e2019-01-24 10:58:23 -0800540 return true;
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800541 });
542 return sparse_array;
543}
544
545static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) {
546 ScopedUtfChars path_utf8(env, path);
547 if (path_utf8.c_str() == nullptr) {
548 // This will throw NPE.
549 return nullptr;
550 }
551
552 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
553 std::unique_ptr<AssetDir> asset_dir =
554 assetmanager->OpenDir(path_utf8.c_str());
555 if (asset_dir == nullptr) {
556 jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str());
557 return nullptr;
558 }
559
560 const size_t file_count = asset_dir->getFileCount();
561
562 jobjectArray array = env->NewObjectArray(file_count, g_stringClass, nullptr);
563 if (array == nullptr) {
564 return nullptr;
565 }
566
567 for (size_t i = 0; i < file_count; i++) {
568 jstring java_string = env->NewStringUTF(asset_dir->getFileName(i).string());
569
570 // Check for errors creating the strings (if malformed or no memory).
571 if (env->ExceptionCheck()) {
572 return nullptr;
573 }
574
575 env->SetObjectArrayElement(array, i, java_string);
576
577 // If we have a large amount of string in our array, we might overflow the
578 // local reference table of the VM.
579 env->DeleteLocalRef(java_string);
580 }
581 return array;
582}
583
584static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
585 jint access_mode) {
586 ScopedUtfChars asset_path_utf8(env, asset_path);
587 if (asset_path_utf8.c_str() == nullptr) {
588 // This will throw NPE.
589 return 0;
590 }
591
592 ATRACE_NAME(base::StringPrintf("AssetManager::OpenAsset(%s)", asset_path_utf8.c_str()).c_str());
593
594 if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
595 access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
596 jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
597 return 0;
598 }
599
600 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
601 std::unique_ptr<Asset> asset =
602 assetmanager->Open(asset_path_utf8.c_str(), static_cast<Asset::AccessMode>(access_mode));
603 if (!asset) {
604 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
605 return 0;
606 }
607 return reinterpret_cast<jlong>(asset.release());
608}
609
610static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
611 jlongArray out_offsets) {
612 ScopedUtfChars asset_path_utf8(env, asset_path);
613 if (asset_path_utf8.c_str() == nullptr) {
614 // This will throw NPE.
615 return nullptr;
616 }
617
618 ATRACE_NAME(base::StringPrintf("AssetManager::OpenAssetFd(%s)", asset_path_utf8.c_str()).c_str());
619
620 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
621 std::unique_ptr<Asset> asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
622 if (!asset) {
623 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
624 return nullptr;
625 }
626 return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
627}
628
629static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
630 jstring asset_path, jint access_mode) {
631 ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
632 ScopedUtfChars asset_path_utf8(env, asset_path);
633 if (asset_path_utf8.c_str() == nullptr) {
634 // This will throw NPE.
635 return 0;
636 }
637
638 ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAsset(%s)", asset_path_utf8.c_str()).c_str());
639
640 if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
641 access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
642 jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
643 return 0;
644 }
645
646 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
647 std::unique_ptr<Asset> asset;
648 if (cookie != kInvalidCookie) {
649 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie,
650 static_cast<Asset::AccessMode>(access_mode));
651 } else {
652 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(),
653 static_cast<Asset::AccessMode>(access_mode));
654 }
655
656 if (!asset) {
657 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
658 return 0;
659 }
660 return reinterpret_cast<jlong>(asset.release());
661}
662
663static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
664 jstring asset_path, jlongArray out_offsets) {
665 ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
666 ScopedUtfChars asset_path_utf8(env, asset_path);
667 if (asset_path_utf8.c_str() == nullptr) {
668 // This will throw NPE.
669 return nullptr;
670 }
671
672 ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAssetFd(%s)", asset_path_utf8.c_str()).c_str());
673
674 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
675 std::unique_ptr<Asset> asset;
676 if (cookie != kInvalidCookie) {
677 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
678 } else {
679 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
680 }
681
682 if (!asset) {
683 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
684 return nullptr;
685 }
686 return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
687}
688
689static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie,
690 jstring asset_path) {
691 ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
692 ScopedUtfChars asset_path_utf8(env, asset_path);
693 if (asset_path_utf8.c_str() == nullptr) {
694 // This will throw NPE.
695 return 0;
696 }
697
698 ATRACE_NAME(base::StringPrintf("AssetManager::OpenXmlAsset(%s)", asset_path_utf8.c_str()).c_str());
699
700 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
701 std::unique_ptr<Asset> asset;
702 if (cookie != kInvalidCookie) {
703 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
704 } else {
705 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie);
706 }
707
708 if (!asset) {
709 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
710 return 0;
711 }
712
713 // May be nullptr.
714 const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie);
715
716 std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(dynamic_ref_table);
717 status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
718 asset.reset();
719
720 if (err != NO_ERROR) {
721 jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
722 return 0;
723 }
724 return reinterpret_cast<jlong>(xml_tree.release());
725}
726
727static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
728 jshort density, jobject typed_value,
729 jboolean resolve_references) {
730 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
731 Res_value value;
732 ResTable_config selected_config;
733 uint32_t flags;
734 ApkAssetsCookie cookie =
735 assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
736 static_cast<uint16_t>(density), &value, &selected_config, &flags);
737 if (cookie == kInvalidCookie) {
738 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
739 }
740
741 uint32_t ref = static_cast<uint32_t>(resid);
742 if (resolve_references) {
743 cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
744 if (cookie == kInvalidCookie) {
745 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
746 }
747 }
748 return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value);
749}
750
751static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
752 jint bag_entry_id, jobject typed_value) {
753 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
754 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
755 if (bag == nullptr) {
756 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
757 }
758
759 uint32_t type_spec_flags = bag->type_spec_flags;
760 ApkAssetsCookie cookie = kInvalidCookie;
761 const Res_value* bag_value = nullptr;
762 for (const ResolvedBag::Entry& entry : bag) {
763 if (entry.key == static_cast<uint32_t>(bag_entry_id)) {
764 cookie = entry.cookie;
765 bag_value = &entry.value;
766
767 // Keep searching (the old implementation did that).
768 }
769 }
770
771 if (cookie == kInvalidCookie) {
772 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
773 }
774
775 Res_value value = *bag_value;
776 uint32_t ref = static_cast<uint32_t>(resid);
777 ResTable_config selected_config;
778 cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref);
779 if (cookie == kInvalidCookie) {
780 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
781 }
782 return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value);
783}
784
785static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
786 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
787 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
788 if (bag == nullptr) {
789 return nullptr;
790 }
791
792 jintArray array = env->NewIntArray(bag->entry_count);
793 if (env->ExceptionCheck()) {
794 return nullptr;
795 }
796
797 for (uint32_t i = 0; i < bag->entry_count; i++) {
798 jint attr_resid = bag->entries[i].key;
799 env->SetIntArrayRegion(array, i, 1, &attr_resid);
800 }
801 return array;
802}
803
804static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr,
805 jint resid) {
806 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
807 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
808 if (bag == nullptr) {
809 return nullptr;
810 }
811
812 jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr);
813 if (array == nullptr) {
814 return nullptr;
815 }
816
817 for (uint32_t i = 0; i < bag->entry_count; i++) {
818 const ResolvedBag::Entry& entry = bag->entries[i];
819
820 // Resolve any references to their final value.
821 Res_value value = entry.value;
822 ResTable_config selected_config;
823 uint32_t flags;
824 uint32_t ref;
825 ApkAssetsCookie cookie =
826 assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
827 if (cookie == kInvalidCookie) {
828 return nullptr;
829 }
830
831 if (value.dataType == Res_value::TYPE_STRING) {
832 const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie];
833 const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool();
834
835 jstring java_string = nullptr;
836 size_t str_len;
837 const char* str_utf8 = pool->string8At(value.data, &str_len);
838 if (str_utf8 != nullptr) {
839 java_string = env->NewStringUTF(str_utf8);
840 } else {
841 const char16_t* str_utf16 = pool->stringAt(value.data, &str_len);
842 java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16), str_len);
843 }
844
845 // Check for errors creating the strings (if malformed or no memory).
846 if (env->ExceptionCheck()) {
847 return nullptr;
848 }
849
850 env->SetObjectArrayElement(array, i, java_string);
851
852 // If we have a large amount of string in our array, we might overflow the
853 // local reference table of the VM.
854 env->DeleteLocalRef(java_string);
855 }
856 }
857 return array;
858}
859
860static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
861 jint resid) {
862 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
863 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
864 if (bag == nullptr) {
865 return nullptr;
866 }
867
868 jintArray array = env->NewIntArray(bag->entry_count * 2);
869 if (array == nullptr) {
870 return nullptr;
871 }
872
873 jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
874 if (buffer == nullptr) {
875 return nullptr;
876 }
877
878 for (size_t i = 0; i < bag->entry_count; i++) {
879 const ResolvedBag::Entry& entry = bag->entries[i];
880 Res_value value = entry.value;
881 ResTable_config selected_config;
882 uint32_t flags;
883 uint32_t ref;
884 ApkAssetsCookie cookie =
885 assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
886 if (cookie == kInvalidCookie) {
887 env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
888 return nullptr;
889 }
890
891 jint string_index = -1;
892 if (value.dataType == Res_value::TYPE_STRING) {
893 string_index = static_cast<jint>(value.data);
894 }
895
896 buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie);
897 buffer[(i * 2) + 1] = string_index;
898 }
899 env->ReleasePrimitiveArrayCritical(array, buffer, 0);
900 return array;
901}
902
903static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
904 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
905 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
906 if (bag == nullptr) {
907 return nullptr;
908 }
909
910 jintArray array = env->NewIntArray(bag->entry_count);
911 if (array == nullptr) {
912 return nullptr;
913 }
914
915 jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
916 if (buffer == nullptr) {
917 return nullptr;
918 }
919
920 for (size_t i = 0; i < bag->entry_count; i++) {
921 const ResolvedBag::Entry& entry = bag->entries[i];
922 Res_value value = entry.value;
923 ResTable_config selected_config;
924 uint32_t flags;
925 uint32_t ref;
926 ApkAssetsCookie cookie =
927 assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
928 if (cookie == kInvalidCookie) {
929 env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
930 return nullptr;
931 }
932
933 if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) {
934 buffer[i] = static_cast<jint>(value.data);
935 }
936 }
937 env->ReleasePrimitiveArrayCritical(array, buffer, 0);
938 return array;
939}
940
941static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) {
942 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
943 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
944 if (bag == nullptr) {
945 return -1;
946 }
947 return static_cast<jint>(bag->entry_count);
948}
949
950static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
951 jintArray out_data) {
952 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
953 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
954 if (bag == nullptr) {
955 return -1;
956 }
957
958 const jsize out_data_length = env->GetArrayLength(out_data);
959 if (env->ExceptionCheck()) {
960 return -1;
961 }
962
963 if (static_cast<jsize>(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) {
964 jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough");
965 return -1;
966 }
967
968 jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_data, nullptr));
969 if (buffer == nullptr) {
970 return -1;
971 }
972
973 jint* cursor = buffer;
974 for (size_t i = 0; i < bag->entry_count; i++) {
975 const ResolvedBag::Entry& entry = bag->entries[i];
976 Res_value value = entry.value;
977 ResTable_config selected_config;
978 selected_config.density = 0;
979 uint32_t flags = bag->type_spec_flags;
Todd Kennedy1d63cc32018-04-18 10:25:45 -0700980 uint32_t ref = 0;
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800981 ApkAssetsCookie cookie =
982 assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
983 if (cookie == kInvalidCookie) {
984 env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT);
985 return -1;
986 }
987
988 // Deal with the special @null value -- it turns back to TYPE_NULL.
989 if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
990 value.dataType = Res_value::TYPE_NULL;
991 value.data = Res_value::DATA_NULL_UNDEFINED;
992 }
993
994 cursor[STYLE_TYPE] = static_cast<jint>(value.dataType);
995 cursor[STYLE_DATA] = static_cast<jint>(value.data);
996 cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
997 cursor[STYLE_RESOURCE_ID] = static_cast<jint>(ref);
998 cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(flags);
999 cursor[STYLE_DENSITY] = static_cast<jint>(selected_config.density);
1000 cursor += STYLE_NUM_ENTRIES;
1001 }
1002 env->ReleasePrimitiveArrayCritical(out_data, buffer, 0);
1003 return static_cast<jint>(bag->entry_count);
1004}
1005
1006static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name,
1007 jstring def_type, jstring def_package) {
1008 ScopedUtfChars name_utf8(env, name);
1009 if (name_utf8.c_str() == nullptr) {
1010 // This will throw NPE.
1011 return 0;
1012 }
1013
1014 std::string type;
1015 if (def_type != nullptr) {
1016 ScopedUtfChars type_utf8(env, def_type);
1017 CHECK(type_utf8.c_str() != nullptr);
1018 type = type_utf8.c_str();
1019 }
1020
1021 std::string package;
1022 if (def_package != nullptr) {
1023 ScopedUtfChars package_utf8(env, def_package);
1024 CHECK(package_utf8.c_str() != nullptr);
1025 package = package_utf8.c_str();
1026 }
1027 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1028 return static_cast<jint>(assetmanager->GetResourceId(name_utf8.c_str(), type, package));
1029}
1030
1031static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
1032 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1033 AssetManager2::ResourceName name;
1034 if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
1035 return nullptr;
1036 }
1037
Winson2f3669b2019-01-11 11:28:34 -08001038 std::string result = ToFormattedResourceString(&name);
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001039 return env->NewStringUTF(result.c_str());
1040}
1041
1042static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
1043 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1044 AssetManager2::ResourceName name;
1045 if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
1046 return nullptr;
1047 }
1048
1049 if (name.package != nullptr) {
1050 return env->NewStringUTF(name.package);
1051 }
1052 return nullptr;
1053}
1054
1055static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
1056 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1057 AssetManager2::ResourceName name;
1058 if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
1059 return nullptr;
1060 }
1061
1062 if (name.type != nullptr) {
1063 return env->NewStringUTF(name.type);
1064 } else if (name.type16 != nullptr) {
1065 return env->NewString(reinterpret_cast<const jchar*>(name.type16), name.type_len);
1066 }
1067 return nullptr;
1068}
1069
1070static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
1071 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1072 AssetManager2::ResourceName name;
1073 if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
1074 return nullptr;
1075 }
1076
1077 if (name.entry != nullptr) {
1078 return env->NewStringUTF(name.entry);
1079 } else if (name.entry16 != nullptr) {
1080 return env->NewString(reinterpret_cast<const jchar*>(name.entry16), name.entry_len);
1081 }
1082 return nullptr;
1083}
1084
Winson2f3669b2019-01-11 11:28:34 -08001085static void NativeSetResourceResolutionLoggingEnabled(JNIEnv* /*env*/,
1086 jclass /*clazz*/,
1087 jlong ptr,
1088 jboolean enabled) {
1089 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1090 assetmanager->SetResourceResolutionLoggingEnabled(enabled);
1091}
1092
1093static jstring NativeGetLastResourceResolution(JNIEnv* env,
1094 jclass /*clazz*/,
1095 jlong ptr) {
1096 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1097 std::string resolution = assetmanager->GetLastResourceResolution();
1098 if (resolution.empty()) {
1099 return nullptr;
1100 } else {
1101 return env->NewStringUTF(resolution.c_str());
1102 }
1103}
1104
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001105static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr,
1106 jboolean exclude_system) {
1107 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1108 std::set<std::string> locales =
1109 assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/);
1110
1111 jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr);
1112 if (array == nullptr) {
1113 return nullptr;
1114 }
1115
1116 size_t idx = 0;
1117 for (const std::string& locale : locales) {
1118 jstring java_string = env->NewStringUTF(locale.c_str());
1119 if (java_string == nullptr) {
1120 return nullptr;
1121 }
1122 env->SetObjectArrayElement(array, idx++, java_string);
1123 env->DeleteLocalRef(java_string);
1124 }
1125 return array;
1126}
1127
1128static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
1129 jobject result =
1130 env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor);
1131 if (result == nullptr) {
1132 return nullptr;
1133 }
1134
1135 env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset,
1136 config.smallestScreenWidthDp);
1137 env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
1138 env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
1139 return result;
1140}
1141
1142static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
1143 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1144 std::set<ResTable_config> configurations =
1145 assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/);
1146
1147 jobjectArray array =
1148 env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr);
1149 if (array == nullptr) {
1150 return nullptr;
1151 }
1152
1153 size_t idx = 0;
1154 for (const ResTable_config& configuration : configurations) {
1155 jobject java_configuration = ConstructConfigurationObject(env, configuration);
1156 if (java_configuration == nullptr) {
1157 return nullptr;
1158 }
1159
1160 env->SetObjectArrayElement(array, idx++, java_configuration);
1161 env->DeleteLocalRef(java_configuration);
1162 }
1163 return array;
1164}
1165
Aurimas Liutikas8f004c82019-01-17 17:20:10 -08001166static jintArray NativeAttributeResolutionStack(
1167 JNIEnv* env, jclass /*clazz*/, jlong ptr,
1168 jlong theme_ptr, jint xml_style_res,
1169 jint def_style_attr, jint def_style_resid) {
1170
1171 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1172 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1173 CHECK(theme->GetAssetManager() == &(*assetmanager));
1174 (void) assetmanager;
1175
1176 // Load default style from attribute, if specified...
1177 uint32_t def_style_flags = 0u;
1178 if (def_style_attr != 0) {
1179 Res_value value;
1180 if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
1181 if (value.dataType == Res_value::TYPE_REFERENCE) {
1182 def_style_resid = value.data;
1183 }
1184 }
1185 }
1186
1187 auto style_stack = assetmanager->GetBagResIdStack(xml_style_res);
1188 auto def_style_stack = assetmanager->GetBagResIdStack(def_style_resid);
1189
1190 jintArray array = env->NewIntArray(style_stack.size() + def_style_stack.size());
1191 if (env->ExceptionCheck()) {
1192 return nullptr;
1193 }
1194
1195 for (uint32_t i = 0; i < style_stack.size(); i++) {
1196 jint attr_resid = style_stack[i];
1197 env->SetIntArrayRegion(array, i, 1, &attr_resid);
1198 }
1199 for (uint32_t i = 0; i < def_style_stack.size(); i++) {
1200 jint attr_resid = def_style_stack[i];
1201 env->SetIntArrayRegion(array, style_stack.size() + i, 1, &attr_resid);
1202 }
1203 return array;
1204}
1205
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001206static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1207 jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr,
1208 jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) {
1209 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1210 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1211 CHECK(theme->GetAssetManager() == &(*assetmanager));
1212 (void) assetmanager;
1213
1214 ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
1215 uint32_t* out_values = reinterpret_cast<uint32_t*>(out_values_ptr);
1216 uint32_t* out_indices = reinterpret_cast<uint32_t*>(out_indices_ptr);
1217
1218 jsize attrs_len = env->GetArrayLength(java_attrs);
1219 jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
1220 if (attrs == nullptr) {
1221 return;
1222 }
1223
1224 ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr),
1225 static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(attrs), attrs_len,
1226 out_values, out_indices);
1227 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1228}
1229
1230static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1231 jint def_style_attr, jint def_style_resid, jintArray java_values,
1232 jintArray java_attrs, jintArray out_java_values,
1233 jintArray out_java_indices) {
1234 const jsize attrs_len = env->GetArrayLength(java_attrs);
1235 const jsize out_values_len = env->GetArrayLength(out_java_values);
1236 if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
1237 jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
1238 return JNI_FALSE;
1239 }
1240
1241 jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
1242 if (attrs == nullptr) {
1243 return JNI_FALSE;
1244 }
1245
1246 jint* values = nullptr;
1247 jsize values_len = 0;
1248 if (java_values != nullptr) {
1249 values_len = env->GetArrayLength(java_values);
1250 values = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_values, nullptr));
1251 if (values == nullptr) {
1252 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1253 return JNI_FALSE;
1254 }
1255 }
1256
1257 jint* out_values =
1258 reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
1259 if (out_values == nullptr) {
1260 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1261 if (values != nullptr) {
1262 env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
1263 }
1264 return JNI_FALSE;
1265 }
1266
1267 jint* out_indices = nullptr;
1268 if (out_java_indices != nullptr) {
1269 jsize out_indices_len = env->GetArrayLength(out_java_indices);
1270 if (out_indices_len > attrs_len) {
1271 out_indices =
1272 reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
1273 if (out_indices == nullptr) {
1274 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1275 if (values != nullptr) {
1276 env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
1277 }
1278 env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
1279 return JNI_FALSE;
1280 }
1281 }
1282 }
1283
1284 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1285 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1286 CHECK(theme->GetAssetManager() == &(*assetmanager));
1287 (void) assetmanager;
1288
1289 bool result = ResolveAttrs(
1290 theme, static_cast<uint32_t>(def_style_attr), static_cast<uint32_t>(def_style_resid),
1291 reinterpret_cast<uint32_t*>(values), values_len, reinterpret_cast<uint32_t*>(attrs),
1292 attrs_len, reinterpret_cast<uint32_t*>(out_values), reinterpret_cast<uint32_t*>(out_indices));
1293 if (out_indices != nullptr) {
1294 env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
1295 }
1296
1297 env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
1298 if (values != nullptr) {
1299 env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
1300 }
1301 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1302 return result ? JNI_TRUE : JNI_FALSE;
1303}
1304
1305static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr,
1306 jlong xml_parser_ptr, jintArray java_attrs,
1307 jintArray out_java_values, jintArray out_java_indices) {
1308 const jsize attrs_len = env->GetArrayLength(java_attrs);
1309 const jsize out_values_len = env->GetArrayLength(out_java_values);
1310 if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
1311 jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
1312 return JNI_FALSE;
1313 }
1314
1315 jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
1316 if (attrs == nullptr) {
1317 return JNI_FALSE;
1318 }
1319
1320 jint* out_values =
1321 reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
1322 if (out_values == nullptr) {
1323 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1324 return JNI_FALSE;
1325 }
1326
1327 jint* out_indices = nullptr;
1328 if (out_java_indices != nullptr) {
1329 jsize out_indices_len = env->GetArrayLength(out_java_indices);
1330 if (out_indices_len > attrs_len) {
1331 out_indices =
1332 reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
1333 if (out_indices == nullptr) {
1334 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1335 env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
1336 return JNI_FALSE;
1337 }
1338 }
1339 }
1340
1341 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1342 ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
1343
1344 bool result = RetrieveAttributes(assetmanager.get(), xml_parser,
1345 reinterpret_cast<uint32_t*>(attrs), attrs_len,
1346 reinterpret_cast<uint32_t*>(out_values),
1347 reinterpret_cast<uint32_t*>(out_indices));
1348
1349 if (out_indices != nullptr) {
1350 env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
1351 }
1352 env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
1353 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1354 return result ? JNI_TRUE : JNI_FALSE;
1355}
1356
1357static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
1358 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1359 return reinterpret_cast<jlong>(assetmanager->NewTheme().release());
1360}
1361
1362static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
1363 delete reinterpret_cast<Theme*>(theme_ptr);
1364}
1365
1366static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1367 jint resid, jboolean force) {
1368 // AssetManager is accessed via the theme, so grab an explicit lock here.
1369 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1370 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1371 CHECK(theme->GetAssetManager() == &(*assetmanager));
1372 (void) assetmanager;
1373 theme->ApplyStyle(static_cast<uint32_t>(resid), force);
1374
1375 // TODO(adamlesinski): Consider surfacing exception when result is failure.
1376 // CTS currently expects no exceptions from this method.
1377 // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid);
1378 // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
1379}
1380
Ryan Mitchellb3ae42e2018-10-16 12:48:38 -07001381static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_asset_manager_ptr,
1382 jlong dst_theme_ptr, jlong src_asset_manager_ptr, jlong src_theme_ptr) {
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001383 Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
1384 Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr);
Ryan Mitchellb3ae42e2018-10-16 12:48:38 -07001385
1386 if (dst_asset_manager_ptr != src_asset_manager_ptr) {
1387 ScopedLock<AssetManager2> dst_assetmanager(AssetManagerFromLong(dst_asset_manager_ptr));
1388 CHECK(dst_theme->GetAssetManager() == &(*dst_assetmanager));
1389 (void) dst_assetmanager;
1390
1391 ScopedLock <AssetManager2> src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr));
1392 CHECK(src_theme->GetAssetManager() == &(*src_assetmanager));
1393 (void) src_assetmanager;
1394
1395 dst_theme->SetTo(*src_theme);
1396 } else {
1397 dst_theme->SetTo(*src_theme);
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001398 }
1399}
1400
1401static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
1402 reinterpret_cast<Theme*>(theme_ptr)->Clear();
1403}
1404
1405static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1406 jint resid, jobject typed_value,
1407 jboolean resolve_references) {
1408 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1409 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1410 CHECK(theme->GetAssetManager() == &(*assetmanager));
1411 (void) assetmanager;
1412
1413 Res_value value;
1414 uint32_t flags;
1415 ApkAssetsCookie cookie = theme->GetAttribute(static_cast<uint32_t>(resid), &value, &flags);
1416 if (cookie == kInvalidCookie) {
1417 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
1418 }
1419
1420 uint32_t ref = 0u;
1421 if (resolve_references) {
1422 ResTable_config selected_config;
1423 cookie =
1424 theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
1425 if (cookie == kInvalidCookie) {
1426 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
1427 }
1428 }
1429 return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value);
1430}
1431
1432static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1433 jint priority, jstring tag, jstring prefix) {
1434 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1435 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1436 CHECK(theme->GetAssetManager() == &(*assetmanager));
1437 (void) assetmanager;
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001438 (void) priority;
1439 (void) tag;
1440 (void) prefix;
Ryan Mitchellb3ae42e2018-10-16 12:48:38 -07001441
1442 theme->Dump();
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001443}
1444
1445static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/,
1446 jlong theme_ptr) {
1447 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1448 return static_cast<jint>(theme->GetChangingConfigurations());
1449}
1450
1451static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
1452 delete reinterpret_cast<Asset*>(asset_ptr);
1453}
1454
1455static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
1456 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1457 uint8_t b;
1458 ssize_t res = asset->read(&b, sizeof(b));
1459 return res == sizeof(b) ? static_cast<jint>(b) : -1;
1460}
1461
1462static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer,
1463 jint offset, jint len) {
1464 if (len == 0) {
1465 return 0;
1466 }
1467
1468 jsize buffer_len = env->GetArrayLength(java_buffer);
1469 if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len ||
1470 offset > buffer_len - len) {
1471 jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
1472 return -1;
1473 }
1474
1475 ScopedByteArrayRW byte_array(env, java_buffer);
1476 if (byte_array.get() == nullptr) {
1477 return -1;
1478 }
1479
1480 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1481 ssize_t res = asset->read(byte_array.get() + offset, len);
1482 if (res < 0) {
1483 jniThrowException(env, "java/io/IOException", "");
1484 return -1;
1485 }
1486 return res > 0 ? static_cast<jint>(res) : -1;
1487}
1488
1489static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset,
1490 jint whence) {
1491 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1492 return static_cast<jlong>(asset->seek(
1493 static_cast<off64_t>(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR))));
1494}
1495
1496static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
1497 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1498 return static_cast<jlong>(asset->getLength());
1499}
1500
1501static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
1502 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1503 return static_cast<jlong>(asset->getRemainingLength());
1504}
1505
1506// ----------------------------------------------------------------------------
1507
1508// JNI registration.
1509static const JNINativeMethod gAssetManagerMethods[] = {
1510 // AssetManager setup methods.
1511 {"nativeCreate", "()J", (void*)NativeCreate},
1512 {"nativeDestroy", "(J)V", (void*)NativeDestroy},
1513 {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
1514 {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V",
1515 (void*)NativeSetConfiguration},
1516 {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;",
1517 (void*)NativeGetAssignedPackageIdentifiers},
1518
1519 // AssetManager file methods.
1520 {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
1521 {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
1522 {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1523 (void*)NativeOpenAssetFd},
1524 {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset},
1525 {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1526 (void*)NativeOpenNonAssetFd},
1527 {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
1528
1529 // AssetManager resource methods.
1530 {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue},
1531 {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I",
1532 (void*)NativeGetResourceBagValue},
1533 {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes},
1534 {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;",
1535 (void*)NativeGetResourceStringArray},
1536 {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo},
1537 {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
1538 {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
1539 {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
1540
1541 // AssetManager resource name/ID methods.
1542 {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
1543 (void*)NativeGetResourceIdentifier},
1544 {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName},
1545 {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName},
1546 {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
1547 {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
Winson2f3669b2019-01-11 11:28:34 -08001548 {"nativeSetResourceResolutionLoggingEnabled", "(JZ)V",
1549 (void*) NativeSetResourceResolutionLoggingEnabled},
1550 {"nativeGetLastResourceResolution", "(J)Ljava/lang/String;",
1551 (void*) NativeGetLastResourceResolution},
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001552 {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
1553 {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
1554 (void*)NativeGetSizeConfigurations},
1555
1556 // Style attribute related methods.
Aurimas Liutikas8f004c82019-01-17 17:20:10 -08001557 {"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack},
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001558 {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
1559 {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
1560 {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
1561
1562 // Theme related methods.
1563 {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
1564 {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy},
1565 {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
Ryan Mitchellb3ae42e2018-10-16 12:48:38 -07001566 {"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy},
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001567 {"nativeThemeClear", "(J)V", (void*)NativeThemeClear},
1568 {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
1569 (void*)NativeThemeGetAttributeValue},
1570 {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
1571 {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations},
1572
1573 // AssetInputStream methods.
1574 {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy},
1575 {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar},
1576 {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead},
1577 {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek},
1578 {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength},
1579 {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
1580
1581 // System/idmap related methods.
1582 {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps},
Mårten Kongstad06a1ac82018-09-20 13:09:47 +02001583 {"nativeCreateIdmapsForStaticOverlaysTargetingAndroid", "()[Ljava/lang/String;",
1584 (void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid},
Mårten Kongstadc92c4dd2019-02-05 01:29:59 +01001585 {"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
1586 (void*)NativeGetOverlayableMap},
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001587
1588 // Global management/debug methods.
1589 {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
1590 {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations},
1591 {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount},
1592};
1593
1594int register_android_content_AssetManager(JNIEnv* env) {
1595 jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets");
1596 gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J");
1597
1598 jclass typedValue = FindClassOrDie(env, "android/util/TypedValue");
1599 gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I");
1600 gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I");
1601 gTypedValueOffsets.mString =
1602 GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;");
1603 gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I");
1604 gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I");
1605 gTypedValueOffsets.mChangingConfigurations =
1606 GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I");
1607 gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
1608
1609 jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
1610 gAssetFileDescriptorOffsets.mFd =
1611 GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
1612 gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
1613 gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
1614
1615 jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
1616 gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
1617
1618 jclass stringClass = FindClassOrDie(env, "java/lang/String");
1619 g_stringClass = MakeGlobalRefOrDie(env, stringClass);
1620
1621 jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
1622 gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
1623 gSparseArrayOffsets.constructor =
1624 GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "<init>", "()V");
1625 gSparseArrayOffsets.put =
1626 GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V");
1627
1628 jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
1629 gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
1630 gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "<init>", "()V");
1631 gConfigurationOffsets.mSmallestScreenWidthDpOffset =
1632 GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I");
1633 gConfigurationOffsets.mScreenWidthDpOffset =
1634 GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I");
1635 gConfigurationOffsets.mScreenHeightDpOffset =
1636 GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I");
1637
Mårten Kongstadc92c4dd2019-02-05 01:29:59 +01001638 jclass arrayMapClass = FindClassOrDie(env, "android/util/ArrayMap");
1639 gArrayMapOffsets.classObject = MakeGlobalRefOrDie(env, arrayMapClass);
1640 gArrayMapOffsets.constructor =
1641 GetMethodIDOrDie(env, gArrayMapOffsets.classObject, "<init>", "()V");
1642 gArrayMapOffsets.put =
1643 GetMethodIDOrDie(env, gArrayMapOffsets.classObject, "put",
1644 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
1645
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001646 return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
1647 NELEM(gAssetManagerMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001648}
1649
1650}; // namespace android