• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 
19 #include <mutex>
20 
21 #include "signal.h"
22 
23 #include "android-base/logging.h"
24 #include "android-base/macros.h"
25 #include "android-base/stringprintf.h"
26 #include "android-base/unique_fd.h"
27 #include "androidfw/ApkAssets.h"
28 #include "utils/misc.h"
29 #include "utils/Trace.h"
30 
31 #include "android_content_res_ApkAssets.h"
32 #include "core_jni_helpers.h"
33 #include "jni.h"
34 #include "nativehelper/ScopedUtfChars.h"
35 
36 using ::android::base::unique_fd;
37 
38 namespace android {
39 
40 static struct overlayableinfo_offsets_t {
41   jclass classObject;
42   jmethodID constructor;
43 } gOverlayableInfoOffsets;
44 
45 static struct assetfiledescriptor_offsets_t {
46   jfieldID mFd;
47   jfieldID mStartOffset;
48   jfieldID mLength;
49 } gAssetFileDescriptorOffsets;
50 
51 static struct assetsprovider_offsets_t {
52   jclass classObject;
53   jmethodID loadAssetFd;
54   jmethodID toString;
55 } gAssetsProviderOffsets;
56 
57 static struct {
58   jmethodID detachFd;
59 } gParcelFileDescriptorOffsets;
60 
61 // Keep in sync with f/b/android/content/res/ApkAssets.java
62 using format_type_t = jint;
63 enum : format_type_t {
64   // The path used to load the apk assets represents an APK file.
65   FORMAT_APK = 0,
66 
67   // The path used to load the apk assets represents an idmap file.
68   FORMAT_IDMAP = 1,
69 
70   // The path used to load the apk assets represents an resources.arsc file.
71   FORMAT_ARSC = 2,
72 
73   // The path used to load the apk assets represents the a directory.
74   FORMAT_DIRECTORY = 3,
75 };
76 
ApkAssetsFromLong(jlong ptr)77 Guarded<std::unique_ptr<const ApkAssets>>& ApkAssetsFromLong(jlong ptr) {
78     return *reinterpret_cast<Guarded<std::unique_ptr<const ApkAssets>>*>(ptr);
79 }
80 
CreateGuardedApkAssets(std::unique_ptr<const ApkAssets> assets)81 static jlong CreateGuardedApkAssets(std::unique_ptr<const ApkAssets> assets) {
82     auto guarded_assets = new Guarded<std::unique_ptr<const ApkAssets>>(std::move(assets));
83     return reinterpret_cast<jlong>(guarded_assets);
84 }
85 
DeleteGuardedApkAssets(Guarded<std::unique_ptr<const ApkAssets>> & apk_assets)86 static void DeleteGuardedApkAssets(Guarded<std::unique_ptr<const ApkAssets>>& apk_assets) {
87     delete &apk_assets;
88 }
89 
90 class LoaderAssetsProvider : public AssetsProvider {
91  public:
Create(JNIEnv * env,jobject assets_provider)92   static std::unique_ptr<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) {
93     return (!assets_provider) ? EmptyAssetsProvider::Create()
94                               : std::unique_ptr<AssetsProvider>(new LoaderAssetsProvider(
95                                     env, assets_provider));
96   }
97 
ForEachFile(const std::string &,const std::function<void (const StringPiece &,FileType)> &) const98   bool ForEachFile(const std::string& /* root_path */,
99                    const std::function<void(const StringPiece&, FileType)>& /* f */) const {
100     return true;
101   }
102 
GetPath() const103   std::optional<std::string_view> GetPath() const override {
104     return {};
105   }
106 
GetDebugName() const107   const std::string& GetDebugName() const override {
108     return debug_name_;
109   }
110 
IsUpToDate() const111   bool IsUpToDate() const override {
112     return true;
113   }
114 
~LoaderAssetsProvider()115   ~LoaderAssetsProvider() override {
116     const auto env = AndroidRuntime::getJNIEnv();
117     CHECK(env != nullptr)  << "Current thread not attached to a Java VM."
118                            << " Failed to close LoaderAssetsProvider.";
119     env->DeleteGlobalRef(assets_provider_);
120   }
121 
122  protected:
OpenInternal(const std::string & path,Asset::AccessMode mode,bool * file_exists) const123   std::unique_ptr<Asset> OpenInternal(const std::string& path,
124                                       Asset::AccessMode mode,
125                                       bool* file_exists) const override {
126     const auto env = AndroidRuntime::getJNIEnv();
127     CHECK(env != nullptr) << "Current thread not attached to a Java VM."
128                           << " ResourcesProvider assets cannot be retrieved on current thread.";
129 
130     jstring java_string = env->NewStringUTF(path.c_str());
131     if (env->ExceptionCheck()) {
132       env->ExceptionDescribe();
133       env->ExceptionClear();
134       return nullptr;
135     }
136 
137     // Check if the AssetsProvider provides a value for the path.
138     jobject asset_fd = env->CallObjectMethod(assets_provider_,
139                                              gAssetsProviderOffsets.loadAssetFd,
140                                              java_string, static_cast<jint>(mode));
141     env->DeleteLocalRef(java_string);
142     if (env->ExceptionCheck()) {
143       env->ExceptionDescribe();
144       env->ExceptionClear();
145       return nullptr;
146     }
147 
148     if (!asset_fd) {
149       if (file_exists) {
150         *file_exists = false;
151       }
152       return nullptr;
153     }
154 
155     const jlong mOffset = env->GetLongField(asset_fd, gAssetFileDescriptorOffsets.mStartOffset);
156     const jlong mLength = env->GetLongField(asset_fd, gAssetFileDescriptorOffsets.mLength);
157     jobject mFd = env->GetObjectField(asset_fd, gAssetFileDescriptorOffsets.mFd);
158     env->DeleteLocalRef(asset_fd);
159 
160     if (!mFd) {
161       jniThrowException(env, "java/lang/NullPointerException", nullptr);
162       env->ExceptionDescribe();
163       env->ExceptionClear();
164       return nullptr;
165     }
166 
167     // Gain ownership of the file descriptor.
168     const jint fd = env->CallIntMethod(mFd, gParcelFileDescriptorOffsets.detachFd);
169     env->DeleteLocalRef(mFd);
170     if (env->ExceptionCheck()) {
171       env->ExceptionDescribe();
172       env->ExceptionClear();
173       return nullptr;
174     }
175 
176     if (file_exists) {
177       *file_exists = true;
178     }
179 
180     return AssetsProvider::CreateAssetFromFd(base::unique_fd(fd),
181                                              nullptr /* path */,
182                                              static_cast<off64_t>(mOffset),
183                                              static_cast<off64_t>(mLength));
184   }
185 
186  private:
187   DISALLOW_COPY_AND_ASSIGN(LoaderAssetsProvider);
188 
LoaderAssetsProvider(JNIEnv * env,jobject assets_provider)189   explicit LoaderAssetsProvider(JNIEnv* env, jobject assets_provider) {
190     assets_provider_ = env->NewGlobalRef(assets_provider);
191     auto string_result = static_cast<jstring>(env->CallObjectMethod(
192         assets_provider_, gAssetsProviderOffsets.toString));
193     ScopedUtfChars str(env, string_result);
194     debug_name_ = std::string(str.c_str(), str.size());
195   }
196 
197   // The global reference to the AssetsProvider
198   jobject assets_provider_;
199   std::string debug_name_;
200 };
201 
NativeLoad(JNIEnv * env,jclass,const format_type_t format,jstring java_path,const jint property_flags,jobject assets_provider)202 static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
203                         jstring java_path, const jint property_flags, jobject assets_provider) {
204   ScopedUtfChars path(env, java_path);
205   if (path.c_str() == nullptr) {
206     return 0;
207   }
208 
209   ATRACE_NAME(base::StringPrintf("LoadApkAssets(%s)", path.c_str()).c_str());
210 
211   auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
212   std::unique_ptr<ApkAssets> apk_assets;
213   switch (format) {
214     case FORMAT_APK: {
215         auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
216                                                   ZipAssetsProvider::Create(path.c_str(),
217                                                                             property_flags));
218         apk_assets = ApkAssets::Load(std::move(assets), property_flags);
219         break;
220     }
221     case FORMAT_IDMAP:
222       apk_assets = ApkAssets::LoadOverlay(path.c_str(), property_flags);
223       break;
224     case FORMAT_ARSC:
225       apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFile(path.c_str()),
226                                         std::move(loader_assets),
227                                         property_flags);
228       break;
229     case FORMAT_DIRECTORY: {
230       auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
231                                                 DirectoryAssetsProvider::Create(path.c_str()));
232       apk_assets = ApkAssets::Load(std::move(assets), property_flags);
233       break;
234     }
235     default:
236       const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
237       jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
238       return 0;
239   }
240 
241   if (apk_assets == nullptr) {
242     const std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str());
243     jniThrowException(env, "java/io/IOException", error_msg.c_str());
244     return 0;
245   }
246   return CreateGuardedApkAssets(std::move(apk_assets));
247 }
248 
NativeLoadFromFd(JNIEnv * env,jclass,const format_type_t format,jobject file_descriptor,jstring friendly_name,const jint property_flags,jobject assets_provider)249 static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
250                               jobject file_descriptor, jstring friendly_name,
251                               const jint property_flags, jobject assets_provider) {
252   ScopedUtfChars friendly_name_utf8(env, friendly_name);
253   if (friendly_name_utf8.c_str() == nullptr) {
254     return 0;
255   }
256 
257   ATRACE_NAME(base::StringPrintf("LoadApkAssetsFd(%s)", friendly_name_utf8.c_str()).c_str());
258 
259   int fd = jniGetFDFromFileDescriptor(env, file_descriptor);
260   if (fd < 0) {
261     jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
262     return 0;
263   }
264 
265   unique_fd dup_fd(::fcntl(fd, F_DUPFD_CLOEXEC, 0));
266   if (dup_fd < 0) {
267     jniThrowIOException(env, errno);
268     return 0;
269   }
270 
271   auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
272   std::unique_ptr<const ApkAssets> apk_assets;
273   switch (format) {
274     case FORMAT_APK: {
275         auto assets =
276                 MultiAssetsProvider::Create(std::move(loader_assets),
277                                             ZipAssetsProvider::Create(std::move(dup_fd),
278                                                                       friendly_name_utf8.c_str(),
279                                                                       property_flags));
280         apk_assets = ApkAssets::Load(std::move(assets), property_flags);
281         break;
282     }
283     case FORMAT_ARSC:
284       apk_assets = ApkAssets::LoadTable(
285           AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */),
286           std::move(loader_assets), property_flags);
287       break;
288     default:
289       const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
290       jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
291       return 0;
292   }
293 
294   if (apk_assets == nullptr) {
295     std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d",
296                                                friendly_name_utf8.c_str(), fd);
297     jniThrowException(env, "java/io/IOException", error_msg.c_str());
298     return 0;
299   }
300   return CreateGuardedApkAssets(std::move(apk_assets));
301 }
302 
NativeLoadFromFdOffset(JNIEnv * env,jclass,const format_type_t format,jobject file_descriptor,jstring friendly_name,const jlong offset,const jlong length,const jint property_flags,jobject assets_provider)303 static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
304                                     jobject file_descriptor, jstring friendly_name,
305                                     const jlong offset, const jlong length,
306                                     const jint property_flags, jobject assets_provider) {
307   ScopedUtfChars friendly_name_utf8(env, friendly_name);
308   if (friendly_name_utf8.c_str() == nullptr) {
309     return 0;
310   }
311 
312   ATRACE_NAME(base::StringPrintf("LoadApkAssetsFd(%s)", friendly_name_utf8.c_str()).c_str());
313 
314   if (offset < 0) {
315     jniThrowException(env, "java/lang/IllegalArgumentException",
316                      "offset cannot be negative");
317     return 0;
318   }
319 
320   if (length < 0) {
321     jniThrowException(env, "java/lang/IllegalArgumentException",
322                      "length cannot be negative");
323     return 0;
324   }
325 
326   int fd = jniGetFDFromFileDescriptor(env, file_descriptor);
327   if (fd < 0) {
328     jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
329     return 0;
330   }
331 
332   unique_fd dup_fd(::fcntl(fd, F_DUPFD_CLOEXEC, 0));
333   if (dup_fd < 0) {
334     jniThrowIOException(env, errno);
335     return 0;
336   }
337 
338   auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
339   std::unique_ptr<const ApkAssets> apk_assets;
340   switch (format) {
341     case FORMAT_APK: {
342         auto assets =
343                 MultiAssetsProvider::Create(std::move(loader_assets),
344                                             ZipAssetsProvider::Create(std::move(dup_fd),
345                                                                       friendly_name_utf8.c_str(),
346                                                                       property_flags,
347                                                                       static_cast<off64_t>(offset),
348                                                                       static_cast<off64_t>(
349                                                                               length)));
350         apk_assets = ApkAssets::Load(std::move(assets), property_flags);
351         break;
352     }
353     case FORMAT_ARSC:
354       apk_assets = ApkAssets::LoadTable(
355           AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */,
356                                             static_cast<off64_t>(offset),
357                                             static_cast<off64_t>(length)),
358           std::move(loader_assets), property_flags);
359       break;
360     default:
361       const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
362       jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
363       return 0;
364   }
365 
366   if (apk_assets == nullptr) {
367     std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d",
368                                                friendly_name_utf8.c_str(), fd);
369     jniThrowException(env, "java/io/IOException", error_msg.c_str());
370     return 0;
371   }
372   return CreateGuardedApkAssets(std::move(apk_assets));
373 }
374 
NativeLoadEmpty(JNIEnv * env,jclass,jint flags,jobject assets_provider)375 static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) {
376   auto apk_assets = ApkAssets::Load(LoaderAssetsProvider::Create(env, assets_provider), flags);
377   return CreateGuardedApkAssets(std::move(apk_assets));
378 }
379 
NativeDestroy(JNIEnv *,jclass,jlong ptr)380 static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
381     DeleteGuardedApkAssets(ApkAssetsFromLong(ptr));
382 }
383 
NativeGetAssetPath(JNIEnv * env,jclass,jlong ptr)384 static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
385     auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
386     auto apk_assets = scoped_apk_assets->get();
387     if (auto path = apk_assets->GetPath()) {
388         return env->NewStringUTF(path->data());
389   }
390   return nullptr;
391 }
392 
NativeGetDebugName(JNIEnv * env,jclass,jlong ptr)393 static jstring NativeGetDebugName(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
394     auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
395     auto apk_assets = scoped_apk_assets->get();
396     return env->NewStringUTF(apk_assets->GetDebugName().c_str());
397 }
398 
NativeGetStringBlock(JNIEnv *,jclass,jlong ptr)399 static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
400     auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
401     auto apk_assets = scoped_apk_assets->get();
402     return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
403 }
404 
NativeIsUpToDate(JNIEnv *,jclass,jlong ptr)405 static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
406     auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
407     auto apk_assets = scoped_apk_assets->get();
408     return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE;
409 }
410 
NativeOpenXml(JNIEnv * env,jclass,jlong ptr,jstring file_name)411 static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) {
412   ScopedUtfChars path_utf8(env, file_name);
413   if (path_utf8.c_str() == nullptr) {
414     return 0;
415   }
416 
417   auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
418   auto apk_assets = scoped_apk_assets->get();
419   std::unique_ptr<Asset> asset = apk_assets->GetAssetsProvider()->Open(
420       path_utf8.c_str(),Asset::AccessMode::ACCESS_RANDOM);
421   if (asset == nullptr) {
422     jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str());
423     return 0;
424   }
425 
426   const auto buffer = asset->getIncFsBuffer(true /* aligned */);
427   const size_t length = asset->getLength();
428   if (!buffer.convert<uint8_t>().verify(length)) {
429       jniThrowException(env, "java/io/FileNotFoundException",
430                         "File not fully present due to incremental installation");
431       return 0;
432   }
433 
434   // DynamicRefTable is only needed when looking up resource references. Opening an XML file
435   // directly from an ApkAssets has no notion of proper resource references.
436   auto xml_tree = util::make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/);
437   status_t err = xml_tree->setTo(buffer.unsafe_ptr(), length, true);
438   if (err != NO_ERROR) {
439     jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
440     return 0;
441   }
442   return reinterpret_cast<jlong>(xml_tree.release());
443 }
444 
NativeGetOverlayableInfo(JNIEnv * env,jclass,jlong ptr,jstring overlayable_name)445 static jobject NativeGetOverlayableInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
446                                          jstring overlayable_name) {
447     auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
448     auto apk_assets = scoped_apk_assets->get();
449 
450     const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
451     if (packages.empty()) {
452         jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
453         return 0;
454   }
455 
456   // TODO(b/119899133): Convert this to a search for the info rather than assuming it's at index 0
457   const auto& overlayable_map = packages[0]->GetOverlayableMap();
458   if (overlayable_map.empty()) {
459     return nullptr;
460   }
461 
462   const char* overlayable_name_native = env->GetStringUTFChars(overlayable_name, nullptr);
463   if (overlayable_name_native == nullptr) {
464       return nullptr;
465   }
466   auto actor = overlayable_map.find(overlayable_name_native);
467   env->ReleaseStringUTFChars(overlayable_name, overlayable_name_native);
468   if (actor == overlayable_map.end()) {
469     return nullptr;
470   }
471 
472   jstring actor_string = env->NewStringUTF(actor->second.c_str());
473   if (env->ExceptionCheck() || actor_string == nullptr) {
474     jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
475     return 0;
476   }
477 
478   return env->NewObject(
479       gOverlayableInfoOffsets.classObject,
480       gOverlayableInfoOffsets.constructor,
481       overlayable_name,
482       actor_string
483   );
484 }
485 
NativeDefinesOverlayable(JNIEnv * env,jclass,jlong ptr)486 static jboolean NativeDefinesOverlayable(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
487     auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
488     auto apk_assets = scoped_apk_assets->get();
489 
490     const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
491     if (packages.empty()) {
492         // Must throw to prevent bypass by returning false
493         jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
494         return 0;
495   }
496 
497   const auto& overlayable_infos = packages[0]->GetOverlayableMap();
498   return overlayable_infos.empty() ? JNI_FALSE : JNI_TRUE;
499 }
500 
501 // JNI registration.
502 static const JNINativeMethod gApkAssetsMethods[] = {
503     {"nativeLoad", "(ILjava/lang/String;ILandroid/content/res/loader/AssetsProvider;)J",
504      (void*)NativeLoad},
505     {"nativeLoadEmpty", "(ILandroid/content/res/loader/AssetsProvider;)J", (void*)NativeLoadEmpty},
506     {"nativeLoadFd",
507      "(ILjava/io/FileDescriptor;Ljava/lang/String;ILandroid/content/res/loader/AssetsProvider;)J",
508      (void*)NativeLoadFromFd},
509     {"nativeLoadFdOffsets",
510      "(ILjava/io/FileDescriptor;Ljava/lang/String;JJILandroid/content/res/loader/AssetsProvider;)J",
511      (void*)NativeLoadFromFdOffset},
512     {"nativeDestroy", "(J)V", (void*)NativeDestroy},
513     {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath},
514     {"nativeGetDebugName", "(J)Ljava/lang/String;", (void*)NativeGetDebugName},
515     {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
516     {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate},
517     {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml},
518     {"nativeGetOverlayableInfo", "(JLjava/lang/String;)Landroid/content/om/OverlayableInfo;",
519      (void*)NativeGetOverlayableInfo},
520     {"nativeDefinesOverlayable", "(J)Z", (void*)NativeDefinesOverlayable},
521 };
522 
register_android_content_res_ApkAssets(JNIEnv * env)523 int register_android_content_res_ApkAssets(JNIEnv* env) {
524   jclass overlayableInfoClass = FindClassOrDie(env, "android/content/om/OverlayableInfo");
525   gOverlayableInfoOffsets.classObject = MakeGlobalRefOrDie(env, overlayableInfoClass);
526   gOverlayableInfoOffsets.constructor = GetMethodIDOrDie(env, gOverlayableInfoOffsets.classObject,
527       "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
528 
529   jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
530   gAssetFileDescriptorOffsets.mFd =
531       GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
532   gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
533   gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
534 
535   jclass assetsProvider = FindClassOrDie(env, "android/content/res/loader/AssetsProvider");
536   gAssetsProviderOffsets.classObject = MakeGlobalRefOrDie(env, assetsProvider);
537   gAssetsProviderOffsets.loadAssetFd = GetMethodIDOrDie(
538       env, gAssetsProviderOffsets.classObject, "loadAssetFd",
539       "(Ljava/lang/String;I)Landroid/content/res/AssetFileDescriptor;");
540   gAssetsProviderOffsets.toString = GetMethodIDOrDie(
541       env, gAssetsProviderOffsets.classObject, "toString", "()Ljava/lang/String;");
542 
543   jclass parcelFd = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
544   gParcelFileDescriptorOffsets.detachFd = GetMethodIDOrDie(env, parcelFd, "detachFd", "()I");
545   return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods,
546                               arraysize(gApkAssetsMethods));
547 }
548 
549 }  // namespace android
550