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