blob: 5c4dc23358222db08026727249f0f276af4215ad [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>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021#include <stdio.h>
Adam Lesinskibde1df22018-02-09 11:12:22 -080022#include <sys/stat.h>
Adam Lesinskibebfcc42018-02-12 14:27:46 -080023#include <sys/types.h>
24#include <sys/wait.h>
Mårten Kongstad06a1ac82018-09-20 13:09:47 +020025#include <unistd.h>
Mårten Kongstad48d22322014-01-31 14:43:27 +010026
Dan Albert46d84442014-11-18 16:07:51 -080027#include <private/android_filesystem_config.h> // for AID_SYSTEM
28
Mårten Kongstad06a1ac82018-09-20 13:09:47 +020029#include <sstream>
30#include <string>
31
Adam Lesinskibebfcc42018-02-12 14:27:46 -080032#include "android-base/logging.h"
33#include "android-base/properties.h"
34#include "android-base/stringprintf.h"
35#include "android_runtime/android_util_AssetManager.h"
Adam Lesinskibde1df22018-02-09 11:12:22 -080036#include "android_runtime/AndroidRuntime.h"
37#include "android_util_Binder.h"
Adam Lesinskibebfcc42018-02-12 14:27:46 -080038#include "androidfw/Asset.h"
39#include "androidfw/AssetManager.h"
40#include "androidfw/AssetManager2.h"
41#include "androidfw/AttributeResolution.h"
42#include "androidfw/MutexGuard.h"
Mårten Kongstad06a1ac82018-09-20 13:09:47 +020043#include "androidfw/PosixUtils.h"
Adam Lesinskibebfcc42018-02-12 14:27:46 -080044#include "androidfw/ResourceTypes.h"
Winson2f3669b2019-01-11 11:28:34 -080045#include "androidfw/ResourceUtils.h"
46
Dan Albert3a091b72014-11-20 15:41:25 -080047#include "core_jni_helpers.h"
48#include "jni.h"
Adam Lesinskibebfcc42018-02-12 14:27:46 -080049#include "nativehelper/JNIHelp.h"
50#include "nativehelper/ScopedPrimitiveArray.h"
51#include "nativehelper/ScopedStringChars.h"
52#include "nativehelper/ScopedUtfChars.h"
Dan Albert46d84442014-11-18 16:07:51 -080053#include "utils/Log.h"
Adam Lesinski11875902017-01-23 12:58:11 -080054#include "utils/misc.h"
Adam Lesinskibde1df22018-02-09 11:12:22 -080055#include "utils/String8.h"
Adam Lesinskibebfcc42018-02-12 14:27:46 -080056#include "utils/Trace.h"
Dan Albert46d84442014-11-18 16:07:51 -080057
Mårten Kongstad48d22322014-01-31 14:43:27 +010058extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
59extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
60
Adam Lesinskibebfcc42018-02-12 14:27:46 -080061using ::android::base::StringPrintf;
Mårten Kongstad06a1ac82018-09-20 13:09:47 +020062using ::android::util::ExecuteBinary;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
64namespace android {
65
66// ----------------------------------------------------------------------------
67
Adam Lesinskibebfcc42018-02-12 14:27:46 -080068static struct typedvalue_offsets_t {
69 jfieldID mType;
70 jfieldID mData;
71 jfieldID mString;
72 jfieldID mAssetCookie;
73 jfieldID mResourceId;
74 jfieldID mChangingConfigurations;
75 jfieldID mDensity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076} gTypedValueOffsets;
77
Adam Lesinskibebfcc42018-02-12 14:27:46 -080078static struct assetfiledescriptor_offsets_t {
79 jfieldID mFd;
80 jfieldID mStartOffset;
81 jfieldID mLength;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082} gAssetFileDescriptorOffsets;
83
Nicolas Geoffraya55a59a2018-05-10 15:00:13 +010084// This is also used by asset_manager.cpp.
85assetmanager_offsets_t gAssetManagerOffsets;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086
Adam Lesinskibebfcc42018-02-12 14:27:46 -080087static struct {
88 jfieldID native_ptr;
89} gApkAssetsFields;
90
91static struct sparsearray_offsets_t {
92 jclass classObject;
93 jmethodID constructor;
94 jmethodID put;
Adam Lesinskide898ff2014-01-29 18:20:45 -080095} gSparseArrayOffsets;
96
Adam Lesinskibebfcc42018-02-12 14:27:46 -080097static struct configuration_offsets_t {
98 jclass classObject;
99 jmethodID constructor;
100 jfieldID mSmallestScreenWidthDpOffset;
101 jfieldID mScreenWidthDpOffset;
102 jfieldID mScreenHeightDpOffset;
Filip Gruszczynski23493322015-07-29 17:02:59 -0700103} gConfigurationOffsets;
104
Mårten Kongstadc92c4dd2019-02-05 01:29:59 +0100105static struct arraymap_offsets_t {
106 jclass classObject;
107 jmethodID constructor;
108 jmethodID put;
109} gArrayMapOffsets;
110
Wei Wangd41a9612019-10-10 11:34:13 -0700111static jclass g_stringClass = nullptr;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112
113// ----------------------------------------------------------------------------
114
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800115// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
116constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
117 return cookie != kInvalidCookie ? static_cast<jint>(cookie + 1) : -1;
118}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800120constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) {
121 return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122}
123
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900124// This is called by zygote (running as user root) as part of preloadResources.
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800125static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) {
126 switch (pid_t pid = fork()) {
127 case -1:
128 PLOG(ERROR) << "failed to fork for idmap";
129 break;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900130
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800131 // child
132 case 0: {
133 struct __user_cap_header_struct capheader;
134 struct __user_cap_data_struct capdata;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900135
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800136 memset(&capheader, 0, sizeof(capheader));
137 memset(&capdata, 0, sizeof(capdata));
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900138
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800139 capheader.version = _LINUX_CAPABILITY_VERSION;
140 capheader.pid = 0;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900141
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800142 if (capget(&capheader, &capdata) != 0) {
143 PLOG(ERROR) << "capget";
144 exit(1);
145 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900146
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800147 capdata.effective = capdata.permitted;
148 if (capset(&capheader, &capdata) != 0) {
149 PLOG(ERROR) << "capset";
150 exit(1);
151 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900152
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800153 if (setgid(AID_SYSTEM) != 0) {
154 PLOG(ERROR) << "setgid";
155 exit(1);
156 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900157
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800158 if (setuid(AID_SYSTEM) != 0) {
159 PLOG(ERROR) << "setuid";
160 exit(1);
161 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900162
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800163 // Generic idmap parameters
Jeongik Chacba95792019-07-09 23:58:01 +0900164 const char* argv[11];
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800165 int argc = 0;
166 struct stat st;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900167
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800168 memset(argv, 0, sizeof(argv));
169 argv[argc++] = AssetManager::IDMAP_BIN;
170 argv[argc++] = "--scan";
171 argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
172 argv[argc++] = AssetManager::TARGET_APK_PATH;
173 argv[argc++] = AssetManager::IDMAP_DIR;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900174
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800175 // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
Mårten Kongstad06a1ac82018-09-20 13:09:47 +0200176 // use VENDOR_OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in
177 // addition to VENDOR_OVERLAY_DIR.
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800178 std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY,
179 "");
180 if (!overlay_theme_path.empty()) {
Mårten Kongstad06a1ac82018-09-20 13:09:47 +0200181 overlay_theme_path =
182 std::string(AssetManager::VENDOR_OVERLAY_DIR) + "/" + overlay_theme_path;
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800183 if (stat(overlay_theme_path.c_str(), &st) == 0) {
184 argv[argc++] = overlay_theme_path.c_str();
Adam Lesinskiec7f06c2018-02-09 11:01:26 -0800185 }
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800186 }
Adam Lesinski11875902017-01-23 12:58:11 -0800187
Mårten Kongstad06a1ac82018-09-20 13:09:47 +0200188 if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
189 argv[argc++] = AssetManager::VENDOR_OVERLAY_DIR;
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800190 }
Adam Lesinski11875902017-01-23 12:58:11 -0800191
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800192 if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
193 argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR;
194 }
195
Jeongik Chacba95792019-07-09 23:58:01 +0900196 if (stat(AssetManager::SYSTEM_EXT_OVERLAY_DIR, &st) == 0) {
197 argv[argc++] = AssetManager::SYSTEM_EXT_OVERLAY_DIR;
Dario Freni4ce46792018-06-01 14:02:08 +0100198 }
199
Mårten Kongstad48c24cf2019-02-25 10:54:09 +0100200 if (stat(AssetManager::ODM_OVERLAY_DIR, &st) == 0) {
201 argv[argc++] = AssetManager::ODM_OVERLAY_DIR;
202 }
203
Mårten Kongstadeb8a5c02019-02-25 14:18:17 +0100204 if (stat(AssetManager::OEM_OVERLAY_DIR, &st) == 0) {
205 argv[argc++] = AssetManager::OEM_OVERLAY_DIR;
206 }
207
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800208 // Finally, invoke idmap (if any overlay directory exists)
209 if (argc > 5) {
210 execv(AssetManager::IDMAP_BIN, (char* const*)argv);
211 PLOG(ERROR) << "failed to execv for idmap";
212 exit(1); // should never get here
213 } else {
214 exit(0);
215 }
216 } break;
217
218 // parent
219 default:
220 waitpid(pid, nullptr, 0);
221 break;
222 }
Adam Lesinski11875902017-01-23 12:58:11 -0800223}
224
Mårten Kongstad06a1ac82018-09-20 13:09:47 +0200225static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* env,
226 jclass /*clazz*/) {
227 // --input-directory can be given multiple times, but idmap2 expects the directory to exist
228 std::vector<std::string> input_dirs;
229 struct stat st;
230 if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
231 input_dirs.push_back(AssetManager::VENDOR_OVERLAY_DIR);
232 }
233
234 if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
235 input_dirs.push_back(AssetManager::PRODUCT_OVERLAY_DIR);
236 }
237
Jeongik Chacba95792019-07-09 23:58:01 +0900238 if (stat(AssetManager::SYSTEM_EXT_OVERLAY_DIR, &st) == 0) {
239 input_dirs.push_back(AssetManager::SYSTEM_EXT_OVERLAY_DIR);
Mårten Kongstad06a1ac82018-09-20 13:09:47 +0200240 }
241
Mårten Kongstad48c24cf2019-02-25 10:54:09 +0100242 if (stat(AssetManager::ODM_OVERLAY_DIR, &st) == 0) {
243 input_dirs.push_back(AssetManager::ODM_OVERLAY_DIR);
244 }
245
Mårten Kongstadeb8a5c02019-02-25 14:18:17 +0100246 if (stat(AssetManager::OEM_OVERLAY_DIR, &st) == 0) {
247 input_dirs.push_back(AssetManager::OEM_OVERLAY_DIR);
248 }
249
Mårten Kongstad06a1ac82018-09-20 13:09:47 +0200250 if (input_dirs.empty()) {
251 LOG(WARNING) << "no directories for idmap2 to scan";
252 return env->NewObjectArray(0, g_stringClass, nullptr);
253 }
254
Yabin Cuia60dc9e2019-02-26 16:12:46 -0800255 if (access("/system/bin/idmap2", X_OK) == -1) {
256 PLOG(WARNING) << "unable to execute idmap2";
257 return nullptr;
258 }
259
Mårten Kongstad06a1ac82018-09-20 13:09:47 +0200260 std::vector<std::string> argv{"/system/bin/idmap2",
261 "scan",
262 "--recursive",
263 "--target-package-name", "android",
264 "--target-apk-path", "/system/framework/framework-res.apk",
265 "--output-directory", "/data/resource-cache"};
266
267 for (const auto& dir : input_dirs) {
268 argv.push_back("--input-directory");
269 argv.push_back(dir);
270 }
271
272 const auto result = ExecuteBinary(argv);
273
274 if (!result) {
275 LOG(ERROR) << "failed to execute idmap2";
276 return nullptr;
277 }
278
279 if (result->status != 0) {
280 LOG(ERROR) << "idmap2: " << result->stderr;
281 return nullptr;
282 }
283
284 std::vector<std::string> idmap_paths;
285 std::istringstream input(result->stdout);
286 std::string path;
287 while (std::getline(input, path)) {
288 idmap_paths.push_back(path);
289 }
290
291 jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr);
292 if (array == nullptr) {
293 return nullptr;
294 }
295 for (size_t i = 0; i < idmap_paths.size(); i++) {
296 const std::string path = idmap_paths[i];
297 jstring java_string = env->NewStringUTF(path.c_str());
298 if (env->ExceptionCheck()) {
299 return nullptr;
300 }
301 env->SetObjectArrayElement(array, i, java_string);
302 env->DeleteLocalRef(java_string);
303 }
304 return array;
305}
306
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800307static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
308 uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
309 env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
310 env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie,
311 ApkAssetsCookieToJavaCookie(cookie));
312 env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data);
313 env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr);
314 env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref);
315 env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags);
316 if (config != nullptr) {
317 env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density);
318 }
319 return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie));
Adam Lesinski11875902017-01-23 12:58:11 -0800320}
321
322// ----------------------------------------------------------------------------
323
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800324// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance.
325struct GuardedAssetManager : public ::AAssetManager {
326 Guarded<AssetManager2> guarded_assetmanager;
Adam Lesinski11875902017-01-23 12:58:11 -0800327};
328
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800329::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
330 jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject);
331 ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle);
332 if (am == nullptr) {
333 jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
334 return nullptr;
335 }
336 return am;
337}
Adam Lesinski11875902017-01-23 12:58:11 -0800338
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800339Guarded<AssetManager2>* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) {
340 if (assetmanager == nullptr) {
341 return nullptr;
342 }
343 return &reinterpret_cast<GuardedAssetManager*>(assetmanager)->guarded_assetmanager;
344}
Adam Lesinski11875902017-01-23 12:58:11 -0800345
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800346Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
347 return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager));
348}
Adam Lesinski11875902017-01-23 12:58:11 -0800349
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800350static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) {
351 return *AssetManagerForNdkAssetManager(reinterpret_cast<AAssetManager*>(ptr));
352}
Adam Lesinski11875902017-01-23 12:58:11 -0800353
Mårten Kongstadc92c4dd2019-02-05 01:29:59 +0100354static jobject NativeGetOverlayableMap(JNIEnv* env, jclass /*clazz*/, jlong ptr,
Ryan Mitchell2e394222019-08-28 12:10:51 -0700355 jstring package_name) {
Mårten Kongstadc92c4dd2019-02-05 01:29:59 +0100356 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
357 const ScopedUtfChars package_name_utf8(env, package_name);
358 CHECK(package_name_utf8.c_str() != nullptr);
359 const std::string std_package_name(package_name_utf8.c_str());
360 const std::unordered_map<std::string, std::string>* map = nullptr;
361
362 assetmanager->ForEachPackage([&](const std::string& this_package_name, uint8_t package_id) {
363 if (this_package_name == std_package_name) {
364 map = assetmanager->GetOverlayableMapForPackage(package_id);
Ryan Mitchella55dc2e2019-01-24 10:58:23 -0800365 return false;
Mårten Kongstadc92c4dd2019-02-05 01:29:59 +0100366 }
Ryan Mitchella55dc2e2019-01-24 10:58:23 -0800367 return true;
Mårten Kongstadc92c4dd2019-02-05 01:29:59 +0100368 });
369
370 if (map == nullptr) {
371 return nullptr;
372 }
373
374 jobject array_map = env->NewObject(gArrayMapOffsets.classObject, gArrayMapOffsets.constructor);
375 if (array_map == nullptr) {
376 return nullptr;
377 }
378
379 for (const auto& iter : *map) {
380 jstring name = env->NewStringUTF(iter.first.c_str());
381 if (env->ExceptionCheck()) {
382 return nullptr;
383 }
384
385 jstring actor = env->NewStringUTF(iter.second.c_str());
386 if (env->ExceptionCheck()) {
387 env->DeleteLocalRef(name);
388 return nullptr;
389 }
390
391 env->CallObjectMethod(array_map, gArrayMapOffsets.put, name, actor);
392
393 env->DeleteLocalRef(name);
394 env->DeleteLocalRef(actor);
395 }
396
397 return array_map;
398}
399
Ryan Mitchell2e394222019-08-28 12:10:51 -0700400static jstring NativeGetOverlayablesToString(JNIEnv* env, jclass /*clazz*/, jlong ptr,
401 jstring package_name) {
402 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
403 const ScopedUtfChars package_name_utf8(env, package_name);
404 CHECK(package_name_utf8.c_str() != nullptr);
405 const std::string std_package_name(package_name_utf8.c_str());
406
407 std::string result;
408 if (!assetmanager->GetOverlayablesToString(std_package_name, &result)) {
409 return nullptr;
410 }
411
412 return env->NewStringUTF(result.c_str());
413}
414
Brett Chabot15c7bcd2019-06-19 10:19:00 -0700415#ifdef __ANDROID__ // Layoutlib does not support parcel
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800416static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
417 jlongArray out_offsets) {
418 off64_t start_offset, length;
419 int fd = asset->openFileDescriptor(&start_offset, &length);
420 asset.reset();
Adam Lesinski11875902017-01-23 12:58:11 -0800421
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800422 if (fd < 0) {
423 jniThrowException(env, "java/io/FileNotFoundException",
424 "This file can not be opened as a file descriptor; it is probably "
425 "compressed");
426 return nullptr;
427 }
Adam Lesinski11875902017-01-23 12:58:11 -0800428
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800429 jlong* offsets = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(out_offsets, 0));
430 if (offsets == nullptr) {
431 close(fd);
432 return nullptr;
433 }
434
435 offsets[0] = start_offset;
436 offsets[1] = length;
437
438 env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0);
439
440 jobject file_desc = jniCreateFileDescriptor(env, fd);
441 if (file_desc == nullptr) {
442 close(fd);
443 return nullptr;
444 }
445 return newParcelFileDescriptor(env, file_desc);
446}
Brett Chabot15c7bcd2019-06-19 10:19:00 -0700447#else
448static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
449 jlongArray out_offsets) {
450 jniThrowException(env, "java/lang/UnsupportedOperationException",
451 "Implement me");
452 // never reached
453 return nullptr;
454}
455#endif
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800456
457static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) {
458 return Asset::getGlobalCount();
459}
460
461static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) {
462 String8 alloc = Asset::getAssetAllocations();
463 if (alloc.length() <= 0) {
464 return nullptr;
465 }
466 return env->NewStringUTF(alloc.string());
467}
468
469static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) {
470 // TODO(adamlesinski): Switch to AssetManager2.
471 return AssetManager::getGlobalCount();
472}
473
474static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) {
475 // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and
476 // AssetManager2 in a contiguous block (GuardedAssetManager).
477 return reinterpret_cast<jlong>(new GuardedAssetManager());
478}
479
480static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
481 delete reinterpret_cast<GuardedAssetManager*>(ptr);
482}
483
484static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
485 jobjectArray apk_assets_array, jboolean invalidate_caches) {
486 ATRACE_NAME("AssetManager::SetApkAssets");
487
488 const jsize apk_assets_len = env->GetArrayLength(apk_assets_array);
489 std::vector<const ApkAssets*> apk_assets;
490 apk_assets.reserve(apk_assets_len);
491 for (jsize i = 0; i < apk_assets_len; i++) {
492 jobject obj = env->GetObjectArrayElement(apk_assets_array, i);
493 if (obj == nullptr) {
494 std::string msg = StringPrintf("ApkAssets at index %d is null", i);
495 jniThrowNullPointerException(env, msg.c_str());
496 return;
497 }
498
499 jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr);
500 if (env->ExceptionCheck()) {
501 return;
502 }
503 apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr));
504 }
505
506 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
507 assetmanager->SetApkAssets(apk_assets, invalidate_caches);
508}
509
510static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
511 jstring locale, jint orientation, jint touchscreen, jint density,
512 jint keyboard, jint keyboard_hidden, jint navigation,
513 jint screen_width, jint screen_height,
514 jint smallest_screen_width_dp, jint screen_width_dp,
515 jint screen_height_dp, jint screen_layout, jint ui_mode,
516 jint color_mode, jint major_version) {
517 ATRACE_NAME("AssetManager::SetConfiguration");
518
519 ResTable_config configuration;
520 memset(&configuration, 0, sizeof(configuration));
521 configuration.mcc = static_cast<uint16_t>(mcc);
522 configuration.mnc = static_cast<uint16_t>(mnc);
523 configuration.orientation = static_cast<uint8_t>(orientation);
524 configuration.touchscreen = static_cast<uint8_t>(touchscreen);
525 configuration.density = static_cast<uint16_t>(density);
526 configuration.keyboard = static_cast<uint8_t>(keyboard);
527 configuration.inputFlags = static_cast<uint8_t>(keyboard_hidden);
528 configuration.navigation = static_cast<uint8_t>(navigation);
529 configuration.screenWidth = static_cast<uint16_t>(screen_width);
530 configuration.screenHeight = static_cast<uint16_t>(screen_height);
531 configuration.smallestScreenWidthDp = static_cast<uint16_t>(smallest_screen_width_dp);
532 configuration.screenWidthDp = static_cast<uint16_t>(screen_width_dp);
533 configuration.screenHeightDp = static_cast<uint16_t>(screen_height_dp);
534 configuration.screenLayout = static_cast<uint8_t>(screen_layout);
535 configuration.uiMode = static_cast<uint8_t>(ui_mode);
536 configuration.colorMode = static_cast<uint8_t>(color_mode);
537 configuration.sdkVersion = static_cast<uint16_t>(major_version);
538
539 if (locale != nullptr) {
540 ScopedUtfChars locale_utf8(env, locale);
541 CHECK(locale_utf8.c_str() != nullptr);
542 configuration.setBcp47Locale(locale_utf8.c_str());
543 }
544
545 // Constants duplicated from Java class android.content.res.Configuration.
546 static const jint kScreenLayoutRoundMask = 0x300;
547 static const jint kScreenLayoutRoundShift = 8;
548
549 // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
550 // in C++. We must extract the round qualifier out of the Java screenLayout and put it
551 // into screenLayout2.
552 configuration.screenLayout2 =
553 static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
554
555 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
556 assetmanager->SetConfiguration(configuration);
557}
558
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800559static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr,
560 jboolean includeOverlays,
561 jboolean includeLoaders) {
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800562 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
563
564 jobject sparse_array =
565 env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor);
566
567 if (sparse_array == nullptr) {
568 // An exception is pending.
569 return nullptr;
570 }
571
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800572 // Optionally exclude overlays and loaders.
573 uint64_t exclusion_flags = ((includeOverlays) ? 0U : PROPERTY_OVERLAY)
574 | ((includeLoaders) ? 0U : PROPERTY_LOADER);
575
Ryan Mitchella55dc2e2019-01-24 10:58:23 -0800576 assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) -> bool {
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800577 jstring jpackage_name = env->NewStringUTF(package_name.c_str());
578 if (jpackage_name == nullptr) {
579 // An exception is pending.
Ryan Mitchella55dc2e2019-01-24 10:58:23 -0800580 return false;
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800581 }
582
583 env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id),
584 jpackage_name);
Ryan Mitchella55dc2e2019-01-24 10:58:23 -0800585 return true;
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800586 }, exclusion_flags);
587
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800588 return sparse_array;
589}
590
591static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) {
592 ScopedUtfChars path_utf8(env, path);
593 if (path_utf8.c_str() == nullptr) {
594 // This will throw NPE.
595 return nullptr;
596 }
597
598 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
599 std::unique_ptr<AssetDir> asset_dir =
600 assetmanager->OpenDir(path_utf8.c_str());
601 if (asset_dir == nullptr) {
602 jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str());
603 return nullptr;
604 }
605
606 const size_t file_count = asset_dir->getFileCount();
607
608 jobjectArray array = env->NewObjectArray(file_count, g_stringClass, nullptr);
609 if (array == nullptr) {
610 return nullptr;
611 }
612
613 for (size_t i = 0; i < file_count; i++) {
614 jstring java_string = env->NewStringUTF(asset_dir->getFileName(i).string());
615
616 // Check for errors creating the strings (if malformed or no memory).
617 if (env->ExceptionCheck()) {
618 return nullptr;
619 }
620
621 env->SetObjectArrayElement(array, i, java_string);
622
623 // If we have a large amount of string in our array, we might overflow the
624 // local reference table of the VM.
625 env->DeleteLocalRef(java_string);
626 }
627 return array;
628}
629
630static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
631 jint access_mode) {
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::OpenAsset(%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 assetmanager->Open(asset_path_utf8.c_str(), static_cast<Asset::AccessMode>(access_mode));
649 if (!asset) {
650 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
651 return 0;
652 }
653 return reinterpret_cast<jlong>(asset.release());
654}
655
656static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
657 jlongArray out_offsets) {
658 ScopedUtfChars asset_path_utf8(env, asset_path);
659 if (asset_path_utf8.c_str() == nullptr) {
660 // This will throw NPE.
661 return nullptr;
662 }
663
664 ATRACE_NAME(base::StringPrintf("AssetManager::OpenAssetFd(%s)", asset_path_utf8.c_str()).c_str());
665
666 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
667 std::unique_ptr<Asset> asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
668 if (!asset) {
669 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
670 return nullptr;
671 }
672 return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
673}
674
675static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
676 jstring asset_path, jint access_mode) {
677 ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
678 ScopedUtfChars asset_path_utf8(env, asset_path);
679 if (asset_path_utf8.c_str() == nullptr) {
680 // This will throw NPE.
681 return 0;
682 }
683
684 ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAsset(%s)", asset_path_utf8.c_str()).c_str());
685
686 if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
687 access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
688 jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
689 return 0;
690 }
691
692 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
693 std::unique_ptr<Asset> asset;
694 if (cookie != kInvalidCookie) {
695 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie,
696 static_cast<Asset::AccessMode>(access_mode));
697 } else {
698 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(),
699 static_cast<Asset::AccessMode>(access_mode));
700 }
701
702 if (!asset) {
703 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
704 return 0;
705 }
706 return reinterpret_cast<jlong>(asset.release());
707}
708
709static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
710 jstring asset_path, jlongArray out_offsets) {
711 ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
712 ScopedUtfChars asset_path_utf8(env, asset_path);
713 if (asset_path_utf8.c_str() == nullptr) {
714 // This will throw NPE.
715 return nullptr;
716 }
717
718 ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAssetFd(%s)", asset_path_utf8.c_str()).c_str());
719
720 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
721 std::unique_ptr<Asset> asset;
722 if (cookie != kInvalidCookie) {
723 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
724 } else {
725 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
726 }
727
728 if (!asset) {
729 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
730 return nullptr;
731 }
732 return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
733}
734
735static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie,
736 jstring asset_path) {
737 ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
738 ScopedUtfChars asset_path_utf8(env, asset_path);
739 if (asset_path_utf8.c_str() == nullptr) {
740 // This will throw NPE.
741 return 0;
742 }
743
744 ATRACE_NAME(base::StringPrintf("AssetManager::OpenXmlAsset(%s)", asset_path_utf8.c_str()).c_str());
745
746 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
747 std::unique_ptr<Asset> asset;
748 if (cookie != kInvalidCookie) {
749 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
750 } else {
751 asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie);
752 }
753
754 if (!asset) {
755 jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
756 return 0;
757 }
758
759 // May be nullptr.
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700760 std::shared_ptr<const DynamicRefTable> dynamic_ref_table =
761 assetmanager->GetDynamicRefTableForCookie(cookie);
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800762
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700763 std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(
764 std::move(dynamic_ref_table));
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800765 status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
766 asset.reset();
767
768 if (err != NO_ERROR) {
769 jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
770 return 0;
771 }
772 return reinterpret_cast<jlong>(xml_tree.release());
773}
774
Winson9947f1e2019-08-16 10:20:39 -0700775static jlong NativeOpenXmlAssetFd(JNIEnv* env, jobject /*clazz*/, jlong ptr, int jcookie,
776 jobject file_descriptor) {
777 int fd = jniGetFDFromFileDescriptor(env, file_descriptor);
778 ATRACE_NAME(base::StringPrintf("AssetManager::OpenXmlAssetFd(%d)", fd).c_str());
779 if (fd < 0) {
780 jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
781 return 0;
782 }
783
784 base::unique_fd dup_fd(::fcntl(fd, F_DUPFD_CLOEXEC, 0));
785 if (dup_fd < 0) {
786 jniThrowIOException(env, errno);
787 return 0;
788 }
789
790 std::unique_ptr<Asset>
791 asset(Asset::createFromFd(dup_fd.release(), nullptr, Asset::AccessMode::ACCESS_BUFFER));
792
793 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
794 ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
795
796 // May be nullptr.
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700797 std::shared_ptr<const DynamicRefTable> dynamic_ref_table =
798 assetmanager->GetDynamicRefTableForCookie(cookie);
Winson9947f1e2019-08-16 10:20:39 -0700799
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700800 std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(
801 std::move(dynamic_ref_table));
Winson9947f1e2019-08-16 10:20:39 -0700802 status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
803 asset.reset();
804
805 if (err != NO_ERROR) {
806 jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
807 return 0;
808 }
809 return reinterpret_cast<jlong>(xml_tree.release());
810}
811
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800812static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
813 jshort density, jobject typed_value,
814 jboolean resolve_references) {
815 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
816 Res_value value;
817 ResTable_config selected_config;
818 uint32_t flags;
819 ApkAssetsCookie cookie =
820 assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
821 static_cast<uint16_t>(density), &value, &selected_config, &flags);
822 if (cookie == kInvalidCookie) {
823 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
824 }
825
826 uint32_t ref = static_cast<uint32_t>(resid);
827 if (resolve_references) {
828 cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
829 if (cookie == kInvalidCookie) {
830 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
831 }
832 }
833 return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value);
834}
835
836static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
837 jint bag_entry_id, jobject typed_value) {
838 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
839 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
840 if (bag == nullptr) {
841 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
842 }
843
844 uint32_t type_spec_flags = bag->type_spec_flags;
845 ApkAssetsCookie cookie = kInvalidCookie;
846 const Res_value* bag_value = nullptr;
847 for (const ResolvedBag::Entry& entry : bag) {
848 if (entry.key == static_cast<uint32_t>(bag_entry_id)) {
849 cookie = entry.cookie;
850 bag_value = &entry.value;
851
852 // Keep searching (the old implementation did that).
853 }
854 }
855
856 if (cookie == kInvalidCookie) {
857 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
858 }
859
860 Res_value value = *bag_value;
861 uint32_t ref = static_cast<uint32_t>(resid);
862 ResTable_config selected_config;
863 cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref);
864 if (cookie == kInvalidCookie) {
865 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
866 }
867 return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value);
868}
869
870static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
871 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
872 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
873 if (bag == nullptr) {
874 return nullptr;
875 }
876
877 jintArray array = env->NewIntArray(bag->entry_count);
878 if (env->ExceptionCheck()) {
879 return nullptr;
880 }
881
882 for (uint32_t i = 0; i < bag->entry_count; i++) {
883 jint attr_resid = bag->entries[i].key;
884 env->SetIntArrayRegion(array, i, 1, &attr_resid);
885 }
886 return array;
887}
888
889static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr,
890 jint resid) {
891 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
892 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
893 if (bag == nullptr) {
894 return nullptr;
895 }
896
897 jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr);
898 if (array == nullptr) {
899 return nullptr;
900 }
901
902 for (uint32_t i = 0; i < bag->entry_count; i++) {
903 const ResolvedBag::Entry& entry = bag->entries[i];
904
905 // Resolve any references to their final value.
906 Res_value value = entry.value;
907 ResTable_config selected_config;
908 uint32_t flags;
909 uint32_t ref;
910 ApkAssetsCookie cookie =
911 assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
912 if (cookie == kInvalidCookie) {
913 return nullptr;
914 }
915
916 if (value.dataType == Res_value::TYPE_STRING) {
917 const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie];
918 const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool();
919
920 jstring java_string = nullptr;
921 size_t str_len;
922 const char* str_utf8 = pool->string8At(value.data, &str_len);
923 if (str_utf8 != nullptr) {
924 java_string = env->NewStringUTF(str_utf8);
925 } else {
926 const char16_t* str_utf16 = pool->stringAt(value.data, &str_len);
927 java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16), str_len);
928 }
929
930 // Check for errors creating the strings (if malformed or no memory).
931 if (env->ExceptionCheck()) {
932 return nullptr;
933 }
934
935 env->SetObjectArrayElement(array, i, java_string);
936
937 // If we have a large amount of string in our array, we might overflow the
938 // local reference table of the VM.
939 env->DeleteLocalRef(java_string);
940 }
941 }
942 return array;
943}
944
945static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
946 jint resid) {
947 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
948 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
949 if (bag == nullptr) {
950 return nullptr;
951 }
952
953 jintArray array = env->NewIntArray(bag->entry_count * 2);
954 if (array == nullptr) {
955 return nullptr;
956 }
957
958 jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
959 if (buffer == nullptr) {
960 return nullptr;
961 }
962
963 for (size_t i = 0; i < bag->entry_count; i++) {
964 const ResolvedBag::Entry& entry = bag->entries[i];
965 Res_value value = entry.value;
966 ResTable_config selected_config;
967 uint32_t flags;
968 uint32_t ref;
969 ApkAssetsCookie cookie =
970 assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
971 if (cookie == kInvalidCookie) {
972 env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
973 return nullptr;
974 }
975
976 jint string_index = -1;
977 if (value.dataType == Res_value::TYPE_STRING) {
978 string_index = static_cast<jint>(value.data);
979 }
980
981 buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie);
982 buffer[(i * 2) + 1] = string_index;
983 }
984 env->ReleasePrimitiveArrayCritical(array, buffer, 0);
985 return array;
986}
987
988static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
989 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
990 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
991 if (bag == nullptr) {
992 return nullptr;
993 }
994
995 jintArray array = env->NewIntArray(bag->entry_count);
996 if (array == nullptr) {
997 return nullptr;
998 }
999
1000 jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
1001 if (buffer == nullptr) {
1002 return nullptr;
1003 }
1004
1005 for (size_t i = 0; i < bag->entry_count; i++) {
1006 const ResolvedBag::Entry& entry = bag->entries[i];
1007 Res_value value = entry.value;
1008 ResTable_config selected_config;
1009 uint32_t flags;
1010 uint32_t ref;
1011 ApkAssetsCookie cookie =
1012 assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
1013 if (cookie == kInvalidCookie) {
1014 env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
1015 return nullptr;
1016 }
1017
1018 if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) {
1019 buffer[i] = static_cast<jint>(value.data);
1020 }
1021 }
1022 env->ReleasePrimitiveArrayCritical(array, buffer, 0);
1023 return array;
1024}
1025
1026static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) {
1027 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1028 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
1029 if (bag == nullptr) {
1030 return -1;
1031 }
1032 return static_cast<jint>(bag->entry_count);
1033}
1034
1035static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
1036 jintArray out_data) {
1037 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1038 const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
1039 if (bag == nullptr) {
1040 return -1;
1041 }
1042
1043 const jsize out_data_length = env->GetArrayLength(out_data);
1044 if (env->ExceptionCheck()) {
1045 return -1;
1046 }
1047
1048 if (static_cast<jsize>(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) {
1049 jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough");
1050 return -1;
1051 }
1052
1053 jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_data, nullptr));
1054 if (buffer == nullptr) {
1055 return -1;
1056 }
1057
1058 jint* cursor = buffer;
1059 for (size_t i = 0; i < bag->entry_count; i++) {
1060 const ResolvedBag::Entry& entry = bag->entries[i];
1061 Res_value value = entry.value;
1062 ResTable_config selected_config;
1063 selected_config.density = 0;
1064 uint32_t flags = bag->type_spec_flags;
Todd Kennedy1d63cc32018-04-18 10:25:45 -07001065 uint32_t ref = 0;
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001066 ApkAssetsCookie cookie =
1067 assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
1068 if (cookie == kInvalidCookie) {
1069 env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT);
1070 return -1;
1071 }
1072
1073 // Deal with the special @null value -- it turns back to TYPE_NULL.
1074 if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
1075 value.dataType = Res_value::TYPE_NULL;
1076 value.data = Res_value::DATA_NULL_UNDEFINED;
1077 }
1078
1079 cursor[STYLE_TYPE] = static_cast<jint>(value.dataType);
1080 cursor[STYLE_DATA] = static_cast<jint>(value.data);
1081 cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
1082 cursor[STYLE_RESOURCE_ID] = static_cast<jint>(ref);
1083 cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(flags);
1084 cursor[STYLE_DENSITY] = static_cast<jint>(selected_config.density);
1085 cursor += STYLE_NUM_ENTRIES;
1086 }
1087 env->ReleasePrimitiveArrayCritical(out_data, buffer, 0);
1088 return static_cast<jint>(bag->entry_count);
1089}
1090
1091static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name,
1092 jstring def_type, jstring def_package) {
1093 ScopedUtfChars name_utf8(env, name);
1094 if (name_utf8.c_str() == nullptr) {
1095 // This will throw NPE.
1096 return 0;
1097 }
1098
1099 std::string type;
1100 if (def_type != nullptr) {
1101 ScopedUtfChars type_utf8(env, def_type);
1102 CHECK(type_utf8.c_str() != nullptr);
1103 type = type_utf8.c_str();
1104 }
1105
1106 std::string package;
1107 if (def_package != nullptr) {
1108 ScopedUtfChars package_utf8(env, def_package);
1109 CHECK(package_utf8.c_str() != nullptr);
1110 package = package_utf8.c_str();
1111 }
1112 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1113 return static_cast<jint>(assetmanager->GetResourceId(name_utf8.c_str(), type, package));
1114}
1115
1116static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
1117 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1118 AssetManager2::ResourceName name;
1119 if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
1120 return nullptr;
1121 }
1122
Winson2f3669b2019-01-11 11:28:34 -08001123 std::string result = ToFormattedResourceString(&name);
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001124 return env->NewStringUTF(result.c_str());
1125}
1126
1127static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
1128 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1129 AssetManager2::ResourceName name;
1130 if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
1131 return nullptr;
1132 }
1133
1134 if (name.package != nullptr) {
1135 return env->NewStringUTF(name.package);
1136 }
1137 return nullptr;
1138}
1139
1140static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
1141 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1142 AssetManager2::ResourceName name;
1143 if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
1144 return nullptr;
1145 }
1146
1147 if (name.type != nullptr) {
1148 return env->NewStringUTF(name.type);
1149 } else if (name.type16 != nullptr) {
1150 return env->NewString(reinterpret_cast<const jchar*>(name.type16), name.type_len);
1151 }
1152 return nullptr;
1153}
1154
1155static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
1156 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1157 AssetManager2::ResourceName name;
1158 if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
1159 return nullptr;
1160 }
1161
1162 if (name.entry != nullptr) {
1163 return env->NewStringUTF(name.entry);
1164 } else if (name.entry16 != nullptr) {
1165 return env->NewString(reinterpret_cast<const jchar*>(name.entry16), name.entry_len);
1166 }
1167 return nullptr;
1168}
1169
Winson2f3669b2019-01-11 11:28:34 -08001170static void NativeSetResourceResolutionLoggingEnabled(JNIEnv* /*env*/,
1171 jclass /*clazz*/,
1172 jlong ptr,
1173 jboolean enabled) {
1174 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1175 assetmanager->SetResourceResolutionLoggingEnabled(enabled);
1176}
1177
1178static jstring NativeGetLastResourceResolution(JNIEnv* env,
1179 jclass /*clazz*/,
1180 jlong ptr) {
1181 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1182 std::string resolution = assetmanager->GetLastResourceResolution();
1183 if (resolution.empty()) {
1184 return nullptr;
1185 } else {
1186 return env->NewStringUTF(resolution.c_str());
1187 }
1188}
1189
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001190static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr,
1191 jboolean exclude_system) {
1192 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1193 std::set<std::string> locales =
1194 assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/);
1195
1196 jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr);
1197 if (array == nullptr) {
1198 return nullptr;
1199 }
1200
1201 size_t idx = 0;
1202 for (const std::string& locale : locales) {
1203 jstring java_string = env->NewStringUTF(locale.c_str());
1204 if (java_string == nullptr) {
1205 return nullptr;
1206 }
1207 env->SetObjectArrayElement(array, idx++, java_string);
1208 env->DeleteLocalRef(java_string);
1209 }
1210 return array;
1211}
1212
1213static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
1214 jobject result =
1215 env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor);
1216 if (result == nullptr) {
1217 return nullptr;
1218 }
1219
1220 env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset,
1221 config.smallestScreenWidthDp);
1222 env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
1223 env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
1224 return result;
1225}
1226
1227static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
1228 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1229 std::set<ResTable_config> configurations =
1230 assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/);
1231
1232 jobjectArray array =
1233 env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr);
1234 if (array == nullptr) {
1235 return nullptr;
1236 }
1237
1238 size_t idx = 0;
1239 for (const ResTable_config& configuration : configurations) {
1240 jobject java_configuration = ConstructConfigurationObject(env, configuration);
1241 if (java_configuration == nullptr) {
1242 return nullptr;
1243 }
1244
1245 env->SetObjectArrayElement(array, idx++, java_configuration);
1246 env->DeleteLocalRef(java_configuration);
1247 }
1248 return array;
1249}
1250
Aurimas Liutikas8f004c82019-01-17 17:20:10 -08001251static jintArray NativeAttributeResolutionStack(
1252 JNIEnv* env, jclass /*clazz*/, jlong ptr,
1253 jlong theme_ptr, jint xml_style_res,
1254 jint def_style_attr, jint def_style_resid) {
1255
1256 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1257 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1258 CHECK(theme->GetAssetManager() == &(*assetmanager));
1259 (void) assetmanager;
1260
1261 // Load default style from attribute, if specified...
1262 uint32_t def_style_flags = 0u;
1263 if (def_style_attr != 0) {
1264 Res_value value;
1265 if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
1266 if (value.dataType == Res_value::TYPE_REFERENCE) {
1267 def_style_resid = value.data;
1268 }
1269 }
1270 }
1271
1272 auto style_stack = assetmanager->GetBagResIdStack(xml_style_res);
1273 auto def_style_stack = assetmanager->GetBagResIdStack(def_style_resid);
1274
1275 jintArray array = env->NewIntArray(style_stack.size() + def_style_stack.size());
1276 if (env->ExceptionCheck()) {
1277 return nullptr;
1278 }
1279
1280 for (uint32_t i = 0; i < style_stack.size(); i++) {
1281 jint attr_resid = style_stack[i];
1282 env->SetIntArrayRegion(array, i, 1, &attr_resid);
1283 }
1284 for (uint32_t i = 0; i < def_style_stack.size(); i++) {
1285 jint attr_resid = def_style_stack[i];
1286 env->SetIntArrayRegion(array, style_stack.size() + i, 1, &attr_resid);
1287 }
1288 return array;
1289}
1290
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001291static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1292 jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr,
1293 jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) {
1294 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1295 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1296 CHECK(theme->GetAssetManager() == &(*assetmanager));
1297 (void) assetmanager;
1298
1299 ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
1300 uint32_t* out_values = reinterpret_cast<uint32_t*>(out_values_ptr);
1301 uint32_t* out_indices = reinterpret_cast<uint32_t*>(out_indices_ptr);
1302
1303 jsize attrs_len = env->GetArrayLength(java_attrs);
1304 jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
1305 if (attrs == nullptr) {
1306 return;
1307 }
1308
1309 ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr),
1310 static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(attrs), attrs_len,
1311 out_values, out_indices);
1312 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1313}
1314
1315static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1316 jint def_style_attr, jint def_style_resid, jintArray java_values,
1317 jintArray java_attrs, jintArray out_java_values,
1318 jintArray out_java_indices) {
1319 const jsize attrs_len = env->GetArrayLength(java_attrs);
1320 const jsize out_values_len = env->GetArrayLength(out_java_values);
1321 if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
1322 jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
1323 return JNI_FALSE;
1324 }
1325
1326 jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
1327 if (attrs == nullptr) {
1328 return JNI_FALSE;
1329 }
1330
1331 jint* values = nullptr;
1332 jsize values_len = 0;
1333 if (java_values != nullptr) {
1334 values_len = env->GetArrayLength(java_values);
1335 values = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_values, nullptr));
1336 if (values == nullptr) {
1337 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1338 return JNI_FALSE;
1339 }
1340 }
1341
1342 jint* out_values =
1343 reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
1344 if (out_values == nullptr) {
1345 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1346 if (values != nullptr) {
1347 env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
1348 }
1349 return JNI_FALSE;
1350 }
1351
1352 jint* out_indices = nullptr;
1353 if (out_java_indices != nullptr) {
1354 jsize out_indices_len = env->GetArrayLength(out_java_indices);
1355 if (out_indices_len > attrs_len) {
1356 out_indices =
1357 reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
1358 if (out_indices == nullptr) {
1359 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1360 if (values != nullptr) {
1361 env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
1362 }
1363 env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
1364 return JNI_FALSE;
1365 }
1366 }
1367 }
1368
1369 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1370 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1371 CHECK(theme->GetAssetManager() == &(*assetmanager));
1372 (void) assetmanager;
1373
1374 bool result = ResolveAttrs(
1375 theme, static_cast<uint32_t>(def_style_attr), static_cast<uint32_t>(def_style_resid),
1376 reinterpret_cast<uint32_t*>(values), values_len, reinterpret_cast<uint32_t*>(attrs),
1377 attrs_len, reinterpret_cast<uint32_t*>(out_values), reinterpret_cast<uint32_t*>(out_indices));
1378 if (out_indices != nullptr) {
1379 env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
1380 }
1381
1382 env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
1383 if (values != nullptr) {
1384 env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
1385 }
1386 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1387 return result ? JNI_TRUE : JNI_FALSE;
1388}
1389
1390static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr,
1391 jlong xml_parser_ptr, jintArray java_attrs,
1392 jintArray out_java_values, jintArray out_java_indices) {
1393 const jsize attrs_len = env->GetArrayLength(java_attrs);
1394 const jsize out_values_len = env->GetArrayLength(out_java_values);
1395 if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
1396 jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
1397 return JNI_FALSE;
1398 }
1399
1400 jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
1401 if (attrs == nullptr) {
1402 return JNI_FALSE;
1403 }
1404
1405 jint* out_values =
1406 reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
1407 if (out_values == nullptr) {
1408 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1409 return JNI_FALSE;
1410 }
1411
1412 jint* out_indices = nullptr;
1413 if (out_java_indices != nullptr) {
1414 jsize out_indices_len = env->GetArrayLength(out_java_indices);
1415 if (out_indices_len > attrs_len) {
1416 out_indices =
1417 reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
1418 if (out_indices == nullptr) {
1419 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1420 env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
1421 return JNI_FALSE;
1422 }
1423 }
1424 }
1425
1426 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1427 ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
1428
1429 bool result = RetrieveAttributes(assetmanager.get(), xml_parser,
1430 reinterpret_cast<uint32_t*>(attrs), attrs_len,
1431 reinterpret_cast<uint32_t*>(out_values),
1432 reinterpret_cast<uint32_t*>(out_indices));
1433
1434 if (out_indices != nullptr) {
1435 env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
1436 }
1437 env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
1438 env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
1439 return result ? JNI_TRUE : JNI_FALSE;
1440}
1441
1442static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
1443 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1444 return reinterpret_cast<jlong>(assetmanager->NewTheme().release());
1445}
1446
1447static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
1448 delete reinterpret_cast<Theme*>(theme_ptr);
1449}
1450
1451static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1452 jint resid, jboolean force) {
1453 // AssetManager is accessed via the theme, so grab an explicit lock here.
1454 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1455 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1456 CHECK(theme->GetAssetManager() == &(*assetmanager));
1457 (void) assetmanager;
1458 theme->ApplyStyle(static_cast<uint32_t>(resid), force);
1459
1460 // TODO(adamlesinski): Consider surfacing exception when result is failure.
1461 // CTS currently expects no exceptions from this method.
1462 // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid);
1463 // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
1464}
1465
Ryan Mitchellb3ae42e2018-10-16 12:48:38 -07001466static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_asset_manager_ptr,
1467 jlong dst_theme_ptr, jlong src_asset_manager_ptr, jlong src_theme_ptr) {
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001468 Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
1469 Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr);
Ryan Mitchellb3ae42e2018-10-16 12:48:38 -07001470
1471 if (dst_asset_manager_ptr != src_asset_manager_ptr) {
1472 ScopedLock<AssetManager2> dst_assetmanager(AssetManagerFromLong(dst_asset_manager_ptr));
1473 CHECK(dst_theme->GetAssetManager() == &(*dst_assetmanager));
1474 (void) dst_assetmanager;
1475
1476 ScopedLock <AssetManager2> src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr));
1477 CHECK(src_theme->GetAssetManager() == &(*src_assetmanager));
1478 (void) src_assetmanager;
1479
1480 dst_theme->SetTo(*src_theme);
1481 } else {
1482 dst_theme->SetTo(*src_theme);
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001483 }
1484}
1485
1486static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
1487 reinterpret_cast<Theme*>(theme_ptr)->Clear();
1488}
1489
1490static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1491 jint resid, jobject typed_value,
1492 jboolean resolve_references) {
1493 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1494 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1495 CHECK(theme->GetAssetManager() == &(*assetmanager));
1496 (void) assetmanager;
1497
1498 Res_value value;
1499 uint32_t flags;
1500 ApkAssetsCookie cookie = theme->GetAttribute(static_cast<uint32_t>(resid), &value, &flags);
1501 if (cookie == kInvalidCookie) {
1502 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
1503 }
1504
1505 uint32_t ref = 0u;
1506 if (resolve_references) {
1507 ResTable_config selected_config;
1508 cookie =
1509 theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
1510 if (cookie == kInvalidCookie) {
1511 return ApkAssetsCookieToJavaCookie(kInvalidCookie);
1512 }
1513 }
1514 return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value);
1515}
1516
1517static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
1518 jint priority, jstring tag, jstring prefix) {
1519 ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
1520 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1521 CHECK(theme->GetAssetManager() == &(*assetmanager));
1522 (void) assetmanager;
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001523 (void) priority;
1524 (void) tag;
1525 (void) prefix;
Ryan Mitchellb3ae42e2018-10-16 12:48:38 -07001526
1527 theme->Dump();
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001528}
1529
1530static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/,
1531 jlong theme_ptr) {
1532 Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
1533 return static_cast<jint>(theme->GetChangingConfigurations());
1534}
1535
1536static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
1537 delete reinterpret_cast<Asset*>(asset_ptr);
1538}
1539
1540static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
1541 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1542 uint8_t b;
1543 ssize_t res = asset->read(&b, sizeof(b));
1544 return res == sizeof(b) ? static_cast<jint>(b) : -1;
1545}
1546
1547static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer,
1548 jint offset, jint len) {
1549 if (len == 0) {
1550 return 0;
1551 }
1552
1553 jsize buffer_len = env->GetArrayLength(java_buffer);
1554 if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len ||
1555 offset > buffer_len - len) {
1556 jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
1557 return -1;
1558 }
1559
1560 ScopedByteArrayRW byte_array(env, java_buffer);
1561 if (byte_array.get() == nullptr) {
1562 return -1;
1563 }
1564
1565 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1566 ssize_t res = asset->read(byte_array.get() + offset, len);
1567 if (res < 0) {
1568 jniThrowException(env, "java/io/IOException", "");
1569 return -1;
1570 }
1571 return res > 0 ? static_cast<jint>(res) : -1;
1572}
1573
1574static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset,
1575 jint whence) {
1576 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1577 return static_cast<jlong>(asset->seek(
1578 static_cast<off64_t>(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR))));
1579}
1580
1581static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
1582 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1583 return static_cast<jlong>(asset->getLength());
1584}
1585
1586static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
1587 Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
1588 return static_cast<jlong>(asset->getRemainingLength());
1589}
1590
1591// ----------------------------------------------------------------------------
1592
1593// JNI registration.
1594static const JNINativeMethod gAssetManagerMethods[] = {
1595 // AssetManager setup methods.
1596 {"nativeCreate", "()J", (void*)NativeCreate},
1597 {"nativeDestroy", "(J)V", (void*)NativeDestroy},
1598 {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
1599 {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V",
1600 (void*)NativeSetConfiguration},
Ryan Mitchell73bfe412019-11-12 16:22:04 -08001601 {"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;",
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001602 (void*)NativeGetAssignedPackageIdentifiers},
1603
1604 // AssetManager file methods.
1605 {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
1606 {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
1607 {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1608 (void*)NativeOpenAssetFd},
1609 {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset},
1610 {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
1611 (void*)NativeOpenNonAssetFd},
1612 {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
Winson9947f1e2019-08-16 10:20:39 -07001613 {"nativeOpenXmlAssetFd", "(JILjava/io/FileDescriptor;)J", (void*)NativeOpenXmlAssetFd},
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001614
1615 // AssetManager resource methods.
1616 {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue},
1617 {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I",
1618 (void*)NativeGetResourceBagValue},
1619 {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes},
1620 {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;",
1621 (void*)NativeGetResourceStringArray},
1622 {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo},
1623 {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
1624 {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
1625 {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
1626
1627 // AssetManager resource name/ID methods.
1628 {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
1629 (void*)NativeGetResourceIdentifier},
1630 {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName},
1631 {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName},
1632 {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
1633 {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
Winson2f3669b2019-01-11 11:28:34 -08001634 {"nativeSetResourceResolutionLoggingEnabled", "(JZ)V",
1635 (void*) NativeSetResourceResolutionLoggingEnabled},
1636 {"nativeGetLastResourceResolution", "(J)Ljava/lang/String;",
1637 (void*) NativeGetLastResourceResolution},
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001638 {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
1639 {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
1640 (void*)NativeGetSizeConfigurations},
1641
1642 // Style attribute related methods.
Aurimas Liutikas8f004c82019-01-17 17:20:10 -08001643 {"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack},
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001644 {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
1645 {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
1646 {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
1647
1648 // Theme related methods.
1649 {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
1650 {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy},
1651 {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
Ryan Mitchellb3ae42e2018-10-16 12:48:38 -07001652 {"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy},
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001653 {"nativeThemeClear", "(J)V", (void*)NativeThemeClear},
1654 {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
1655 (void*)NativeThemeGetAttributeValue},
1656 {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
1657 {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations},
1658
1659 // AssetInputStream methods.
1660 {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy},
1661 {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar},
1662 {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead},
1663 {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek},
1664 {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength},
1665 {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
1666
1667 // System/idmap related methods.
1668 {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps},
Mårten Kongstad06a1ac82018-09-20 13:09:47 +02001669 {"nativeCreateIdmapsForStaticOverlaysTargetingAndroid", "()[Ljava/lang/String;",
1670 (void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid},
Mårten Kongstadc92c4dd2019-02-05 01:29:59 +01001671 {"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
1672 (void*)NativeGetOverlayableMap},
Ryan Mitchell2e394222019-08-28 12:10:51 -07001673 {"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;",
1674 (void*)NativeGetOverlayablesToString},
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001675
1676 // Global management/debug methods.
1677 {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
1678 {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations},
1679 {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount},
1680};
1681
1682int register_android_content_AssetManager(JNIEnv* env) {
1683 jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets");
1684 gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J");
1685
1686 jclass typedValue = FindClassOrDie(env, "android/util/TypedValue");
1687 gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I");
1688 gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I");
1689 gTypedValueOffsets.mString =
1690 GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;");
1691 gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I");
1692 gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I");
1693 gTypedValueOffsets.mChangingConfigurations =
1694 GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I");
1695 gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
1696
1697 jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
1698 gAssetFileDescriptorOffsets.mFd =
1699 GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
1700 gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
1701 gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
1702
1703 jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
1704 gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
1705
1706 jclass stringClass = FindClassOrDie(env, "java/lang/String");
1707 g_stringClass = MakeGlobalRefOrDie(env, stringClass);
1708
1709 jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
1710 gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
1711 gSparseArrayOffsets.constructor =
1712 GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "<init>", "()V");
1713 gSparseArrayOffsets.put =
1714 GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V");
1715
1716 jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
1717 gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
1718 gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "<init>", "()V");
1719 gConfigurationOffsets.mSmallestScreenWidthDpOffset =
1720 GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I");
1721 gConfigurationOffsets.mScreenWidthDpOffset =
1722 GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I");
1723 gConfigurationOffsets.mScreenHeightDpOffset =
1724 GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I");
1725
Mårten Kongstadc92c4dd2019-02-05 01:29:59 +01001726 jclass arrayMapClass = FindClassOrDie(env, "android/util/ArrayMap");
1727 gArrayMapOffsets.classObject = MakeGlobalRefOrDie(env, arrayMapClass);
1728 gArrayMapOffsets.constructor =
1729 GetMethodIDOrDie(env, gArrayMapOffsets.classObject, "<init>", "()V");
1730 gArrayMapOffsets.put =
1731 GetMethodIDOrDie(env, gArrayMapOffsets.classObject, "put",
1732 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
1733
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001734 return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
1735 NELEM(gAssetManagerMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001736}
1737
1738}; // namespace android