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   auto overlayable_name_native = std::string(env->GetStringUTFChars(overlayable_name, NULL));
463   auto actor = overlayable_map.find(overlayable_name_native);
464   if (actor == overlayable_map.end()) {
465     return nullptr;
466   }
467 
468   jstring actor_string = env->NewStringUTF(actor->second.c_str());
469   if (env->ExceptionCheck() || actor_string == nullptr) {
470     jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
471     return 0;
472   }
473 
474   return env->NewObject(
475       gOverlayableInfoOffsets.classObject,
476       gOverlayableInfoOffsets.constructor,
477       overlayable_name,
478       actor_string
479   );
480 }
481 
NativeDefinesOverlayable(JNIEnv * env,jclass,jlong ptr)482 static jboolean NativeDefinesOverlayable(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
483     auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
484     auto apk_assets = scoped_apk_assets->get();
485 
486     const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
487     if (packages.empty()) {
488         // Must throw to prevent bypass by returning false
489         jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
490         return 0;
491   }
492 
493   const auto& overlayable_infos = packages[0]->GetOverlayableMap();
494   return overlayable_infos.empty() ? JNI_FALSE : JNI_TRUE;
495 }
496 
497 // JNI registration.
498 static const JNINativeMethod gApkAssetsMethods[] = {
499     {"nativeLoad", "(ILjava/lang/String;ILandroid/content/res/loader/AssetsProvider;)J",
500      (void*)NativeLoad},
501     {"nativeLoadEmpty", "(ILandroid/content/res/loader/AssetsProvider;)J", (void*)NativeLoadEmpty},
502     {"nativeLoadFd",
503      "(ILjava/io/FileDescriptor;Ljava/lang/String;ILandroid/content/res/loader/AssetsProvider;)J",
504      (void*)NativeLoadFromFd},
505     {"nativeLoadFdOffsets",
506      "(ILjava/io/FileDescriptor;Ljava/lang/String;JJILandroid/content/res/loader/AssetsProvider;)J",
507      (void*)NativeLoadFromFdOffset},
508     {"nativeDestroy", "(J)V", (void*)NativeDestroy},
509     {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath},
510     {"nativeGetDebugName", "(J)Ljava/lang/String;", (void*)NativeGetDebugName},
511     {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
512     {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate},
513     {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml},
514     {"nativeGetOverlayableInfo", "(JLjava/lang/String;)Landroid/content/om/OverlayableInfo;",
515      (void*)NativeGetOverlayableInfo},
516     {"nativeDefinesOverlayable", "(J)Z", (void*)NativeDefinesOverlayable},
517 };
518 
register_android_content_res_ApkAssets(JNIEnv * env)519 int register_android_content_res_ApkAssets(JNIEnv* env) {
520   jclass overlayableInfoClass = FindClassOrDie(env, "android/content/om/OverlayableInfo");
521   gOverlayableInfoOffsets.classObject = MakeGlobalRefOrDie(env, overlayableInfoClass);
522   gOverlayableInfoOffsets.constructor = GetMethodIDOrDie(env, gOverlayableInfoOffsets.classObject,
523       "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
524 
525   jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
526   gAssetFileDescriptorOffsets.mFd =
527       GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
528   gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
529   gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
530 
531   jclass assetsProvider = FindClassOrDie(env, "android/content/res/loader/AssetsProvider");
532   gAssetsProviderOffsets.classObject = MakeGlobalRefOrDie(env, assetsProvider);
533   gAssetsProviderOffsets.loadAssetFd = GetMethodIDOrDie(
534       env, gAssetsProviderOffsets.classObject, "loadAssetFd",
535       "(Ljava/lang/String;I)Landroid/content/res/AssetFileDescriptor;");
536   gAssetsProviderOffsets.toString = GetMethodIDOrDie(
537       env, gAssetsProviderOffsets.classObject, "toString", "()Ljava/lang/String;");
538 
539   jclass parcelFd = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
540   gParcelFileDescriptorOffsets.detachFd = GetMethodIDOrDie(env, parcelFd, "detachFd", "()I");
541   return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods,
542                               arraysize(gApkAssetsMethods));
543 }
544 
545 }  // namespace android
546