| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <dlfcn.h> |
| |
| #include <optional> |
| |
| #define LOG_TAG "OverlayManagerImpl" |
| |
| #include "android-base/no_destructor.h" |
| #include "androidfw/ResourceTypes.h" |
| #include "core_jni_helpers.h" |
| #include "jni.h" |
| |
| namespace android { |
| |
| static struct fabricated_overlay_internal_offsets_t { |
| jclass classObject; |
| jfieldID packageName; |
| jfieldID overlayName; |
| jfieldID targetPackageName; |
| jfieldID targetOverlayable; |
| jfieldID entries; |
| } gFabricatedOverlayInternalOffsets; |
| |
| static struct fabricated_overlay_internal_entry_offsets_t { |
| jclass classObject; |
| jfieldID resourceName; |
| jfieldID dataType; |
| jfieldID data; |
| jfieldID stringData; |
| jfieldID binaryData; |
| jfieldID configuration; |
| jfieldID binaryDataOffset; |
| jfieldID binaryDataSize; |
| jfieldID isNinePatch; |
| } gFabricatedOverlayInternalEntryOffsets; |
| |
| static struct parcel_file_descriptor_offsets_t { |
| jclass classObject; |
| jmethodID getFd; |
| } gParcelFileDescriptorOffsets; |
| |
| static struct List_offsets_t { |
| jclass classObject; |
| jmethodID size; |
| jmethodID get; |
| } gListOffsets; |
| |
| static struct fabricated_overlay_info_offsets_t { |
| jclass classObject; |
| jmethodID constructor; |
| jfieldID packageName; |
| jfieldID overlayName; |
| jfieldID targetPackageName; |
| jfieldID targetOverlayable; |
| jfieldID path; |
| } gFabricatedOverlayInfoOffsets; |
| |
| namespace self_targeting { |
| |
| constexpr const char kIOException[] = "java/io/IOException"; |
| constexpr const char IllegalArgumentException[] = "java/lang/IllegalArgumentException"; |
| |
| class DynamicLibraryLoader { |
| public: |
| explicit DynamicLibraryLoader(JNIEnv* env) { |
| /* For SelfTargeting, there are 2 types of files to be handled. One is frro and the other is |
| * idmap. For creating frro/idmap files and reading frro files, it needs libandroid_runtime |
| * to do a shared link to libidmap2. However, libidmap2 contains the codes generated from |
| * google protocol buffer. When libandroid_runtime does a shared link to libidmap2, it will |
| * impact the memory for system_server and zygote(a.k.a. all applications). |
| * |
| * Not all applications need to either create/read frro files or create idmap files all the |
| * time. When the apps apply the SelfTargeting overlay effect, it only needs libandroifw |
| * that is loaded. To use dlopen(libidmap2.so) is to make sure that applications don't |
| * impact themselves' memory by loading libidmap2 until they need to create/read frro files |
| * or create idmap files. |
| */ |
| handle_ = dlopen("libidmap2.so", RTLD_NOW); |
| if (handle_ == nullptr) { |
| jniThrowNullPointerException(env); |
| return; |
| } |
| |
| createIdmapFileFuncPtr_ = |
| reinterpret_cast<CreateIdmapFileFunc>(dlsym(handle_, "CreateIdmapFile")); |
| if (createIdmapFileFuncPtr_ == nullptr) { |
| jniThrowNullPointerException(env, "The symbol CreateIdmapFile is not found."); |
| return; |
| } |
| getFabricatedOverlayInfoFuncPtr_ = reinterpret_cast<GetFabricatedOverlayInfoFunc>( |
| dlsym(handle_, "GetFabricatedOverlayInfo")); |
| if (getFabricatedOverlayInfoFuncPtr_ == nullptr) { |
| jniThrowNullPointerException(env, "The symbol GetFabricatedOverlayInfo is not found."); |
| return; |
| } |
| createFrroFile_ = reinterpret_cast<CreateFrroFileFunc>(dlsym(handle_, "CreateFrroFile")); |
| if (createFrroFile_ == nullptr) { |
| jniThrowNullPointerException(env, "The symbol CreateFrroFile is not found."); |
| return; |
| } |
| } |
| |
| bool callCreateFrroFile(std::string& out_error, const std::string& packageName, |
| const std::string& overlayName, const std::string& targetPackageName, |
| const std::optional<std::string>& targetOverlayable, |
| const std::vector<FabricatedOverlayEntryParameters>& entries_params, |
| const std::string& frro_file_path) { |
| return createFrroFile_(out_error, packageName, overlayName, targetPackageName, |
| targetOverlayable, entries_params, frro_file_path); |
| } |
| |
| bool callCreateIdmapFile(std::string& out_error, const std::string& targetPath, |
| const std::string& overlayPath, const std::string& idmapPath, |
| const std::string& overlayName, const bool isSystem, |
| const bool isVendor, const bool isProduct, |
| const bool isTargetSignature, const bool isOdm, const bool isOem) { |
| return createIdmapFileFuncPtr_(out_error, targetPath, overlayPath, idmapPath, overlayName, |
| isSystem, isVendor, isProduct, isTargetSignature, isOdm, |
| isOem); |
| } |
| |
| bool callGetFabricatedOverlayInfo(std::string& out_error, const std::string& overlay_path, |
| OverlayManifestInfo& out_overlay_manifest_info) { |
| return getFabricatedOverlayInfoFuncPtr_(out_error, overlay_path, out_overlay_manifest_info); |
| } |
| |
| explicit operator bool() const { |
| return handle_ != nullptr && createFrroFile_ != nullptr && |
| createIdmapFileFuncPtr_ != nullptr && getFabricatedOverlayInfoFuncPtr_ != nullptr; |
| } |
| |
| DynamicLibraryLoader(const DynamicLibraryLoader&) = delete; |
| |
| DynamicLibraryLoader& operator=(const DynamicLibraryLoader&) = delete; |
| |
| ~DynamicLibraryLoader() { |
| if (handle_ != nullptr) { |
| dlclose(handle_); |
| } |
| } |
| |
| private: |
| typedef bool (*CreateFrroFileFunc)( |
| std::string& out_error, const std::string& packageName, const std::string& overlayName, |
| const std::string& targetPackageName, |
| const std::optional<std::string>& targetOverlayable, |
| const std::vector<FabricatedOverlayEntryParameters>& entries_params, |
| const std::string& frro_file_path); |
| |
| typedef bool (*CreateIdmapFileFunc)(std::string& out_error, const std::string& targetPath, |
| const std::string& overlayPath, |
| const std::string& idmapPath, |
| const std::string& overlayName, const jboolean isSystem, |
| const jboolean isVendor, const jboolean isProduct, |
| const jboolean isSameWithTargetSignature, |
| const jboolean isOdm, const jboolean isOem); |
| |
| typedef bool (*GetFabricatedOverlayInfoFunc)(std::string& out_error, |
| const std::string& overlay_path, |
| OverlayManifestInfo& out_overlay_manifest_info); |
| |
| void* handle_; |
| CreateFrroFileFunc createFrroFile_; |
| CreateIdmapFileFunc createIdmapFileFuncPtr_; |
| GetFabricatedOverlayInfoFunc getFabricatedOverlayInfoFuncPtr_; |
| }; |
| |
| static DynamicLibraryLoader& EnsureDynamicLibraryLoader(JNIEnv* env) { |
| static android::base::NoDestructor<DynamicLibraryLoader> loader(env); |
| return *loader; |
| } |
| |
| static std::optional<std::string> getNullableString(JNIEnv* env, jobject object, jfieldID field) { |
| auto javaString = reinterpret_cast<jstring>(env->GetObjectField(object, field)); |
| if (javaString == nullptr) { |
| return std::nullopt; |
| } |
| |
| const ScopedUtfChars result(env, javaString); |
| if (result.c_str() == nullptr) { |
| return std::nullopt; |
| } |
| |
| return std::optional<std::string>{result.c_str()}; |
| } |
| |
| static std::optional<android::base::borrowed_fd> getNullableFileDescriptor(JNIEnv* env, |
| jobject object, |
| jfieldID field) { |
| auto binaryData = env->GetObjectField(object, field); |
| if (binaryData == nullptr) { |
| return std::nullopt; |
| } |
| |
| return env->CallIntMethod(binaryData, gParcelFileDescriptorOffsets.getFd); |
| } |
| |
| static void CreateFrroFile(JNIEnv* env, jclass /*clazz*/, jstring jsFrroFilePath, jobject overlay) { |
| DynamicLibraryLoader& dlLoader = EnsureDynamicLibraryLoader(env); |
| if (!dlLoader) { |
| jniThrowNullPointerException(env, "libidmap2 is not loaded"); |
| return; |
| } |
| |
| if (overlay == nullptr) { |
| jniThrowNullPointerException(env, "overlay is null"); |
| return; |
| } |
| auto jsPackageName = |
| (jstring)env->GetObjectField(overlay, gFabricatedOverlayInternalOffsets.packageName); |
| const ScopedUtfChars packageName(env, jsPackageName); |
| if (packageName.c_str() == nullptr) { |
| jniThrowNullPointerException(env, "packageName is null"); |
| return; |
| } |
| auto jsOverlayName = |
| (jstring)env->GetObjectField(overlay, gFabricatedOverlayInternalOffsets.overlayName); |
| const ScopedUtfChars overlayName(env, jsOverlayName); |
| if (overlayName.c_str() == nullptr) { |
| jniThrowNullPointerException(env, "overlayName is null"); |
| return; |
| } |
| auto jsTargetPackageName = |
| (jstring)env->GetObjectField(overlay, |
| gFabricatedOverlayInternalOffsets.targetPackageName); |
| const ScopedUtfChars targetPackageName(env, jsTargetPackageName); |
| if (targetPackageName.c_str() == nullptr) { |
| jniThrowNullPointerException(env, "targetPackageName is null"); |
| return; |
| } |
| auto overlayable = |
| getNullableString(env, overlay, gFabricatedOverlayInternalOffsets.targetOverlayable); |
| const ScopedUtfChars frroFilePath(env, jsFrroFilePath); |
| if (frroFilePath.c_str() == nullptr) { |
| jniThrowNullPointerException(env, "frroFilePath is null"); |
| return; |
| } |
| jobject entries = env->GetObjectField(overlay, gFabricatedOverlayInternalOffsets.entries); |
| if (entries == nullptr) { |
| jniThrowNullPointerException(env, "overlay entries is null"); |
| return; |
| } |
| const jint size = env->CallIntMethod(entries, gListOffsets.size); |
| ALOGV("frroFilePath = %s, packageName = %s, overlayName = %s, targetPackageName = %s," |
| " targetOverlayable = %s, size = %d", |
| frroFilePath.c_str(), packageName.c_str(), overlayName.c_str(), targetPackageName.c_str(), |
| overlayable.value_or(std::string()).c_str(), size); |
| |
| std::vector<FabricatedOverlayEntryParameters> entries_params; |
| for (jint i = 0; i < size; i++) { |
| jobject entry = env->CallObjectMethod(entries, gListOffsets.get, i); |
| auto jsResourceName = reinterpret_cast<jstring>( |
| env->GetObjectField(entry, gFabricatedOverlayInternalEntryOffsets.resourceName)); |
| const ScopedUtfChars resourceName(env, jsResourceName); |
| const jint dataType = |
| env->GetIntField(entry, gFabricatedOverlayInternalEntryOffsets.dataType); |
| |
| // In Java, the data type is int but the maximum value of data Type is less than 0xff. |
| if (dataType >= static_cast<jint>(UCHAR_MAX)) { |
| jniThrowException(env, IllegalArgumentException, "Unsupported data type"); |
| return; |
| } |
| |
| const auto data = env->GetIntField(entry, gFabricatedOverlayInternalEntryOffsets.data); |
| auto configuration = |
| getNullableString(env, entry, gFabricatedOverlayInternalEntryOffsets.configuration); |
| auto string_data = |
| getNullableString(env, entry, gFabricatedOverlayInternalEntryOffsets.stringData); |
| auto binary_data = |
| getNullableFileDescriptor(env, entry, |
| gFabricatedOverlayInternalEntryOffsets.binaryData); |
| |
| const auto data_offset = |
| env->GetLongField(entry, gFabricatedOverlayInternalEntryOffsets.binaryDataOffset); |
| const auto data_size = |
| env->GetLongField(entry, gFabricatedOverlayInternalEntryOffsets.binaryDataSize); |
| const auto nine_patch = |
| env->GetBooleanField(entry, gFabricatedOverlayInternalEntryOffsets.isNinePatch); |
| entries_params.push_back( |
| FabricatedOverlayEntryParameters{resourceName.c_str(), (DataType)dataType, |
| (DataValue)data, |
| string_data.value_or(std::string()), binary_data, |
| static_cast<off64_t>(data_offset), |
| static_cast<size_t>(data_size), |
| configuration.value_or(std::string()), |
| static_cast<bool>(nine_patch)}); |
| ALOGV("resourceName = %s, dataType = 0x%08x, data = 0x%08x, dataString = %s," |
| " binaryData = %d, configuration = %s", |
| resourceName.c_str(), dataType, data, string_data.value_or(std::string()).c_str(), |
| binary_data.has_value(), configuration.value_or(std::string()).c_str()); |
| } |
| |
| std::string err_result; |
| if (!dlLoader.callCreateFrroFile(err_result, packageName.c_str(), overlayName.c_str(), |
| targetPackageName.c_str(), overlayable, entries_params, |
| frroFilePath.c_str())) { |
| jniThrowException(env, IllegalArgumentException, err_result.c_str()); |
| return; |
| } |
| } |
| |
| static void CreateIdmapFile(JNIEnv* env, jclass /* clazz */, jstring jsTargetPath, |
| jstring jsOverlayPath, jstring jsIdmapPath, jstring jsOverlayName, |
| jboolean isSystem, jboolean isVendor, jboolean isProduct, |
| jboolean isTargetSignature, jboolean isOdm, jboolean isOem) { |
| DynamicLibraryLoader& dlLoader = EnsureDynamicLibraryLoader(env); |
| if (!dlLoader) { |
| jniThrowNullPointerException(env, "libidmap2 is not loaded"); |
| return; |
| } |
| |
| const ScopedUtfChars targetPath(env, jsTargetPath); |
| if (targetPath.c_str() == nullptr) { |
| jniThrowNullPointerException(env, "targetPath is null"); |
| return; |
| } |
| const ScopedUtfChars overlayPath(env, jsOverlayPath); |
| if (overlayPath.c_str() == nullptr) { |
| jniThrowNullPointerException(env, "overlayPath is null"); |
| return; |
| } |
| const ScopedUtfChars idmapPath(env, jsIdmapPath); |
| if (idmapPath.c_str() == nullptr) { |
| jniThrowNullPointerException(env, "idmapPath is null"); |
| return; |
| } |
| const ScopedUtfChars overlayName(env, jsOverlayName); |
| if (overlayName.c_str() == nullptr) { |
| jniThrowNullPointerException(env, "overlayName is null"); |
| return; |
| } |
| ALOGV("target_path = %s, overlay_path = %s, idmap_path = %s, overlay_name = %s", |
| targetPath.c_str(), overlayPath.c_str(), idmapPath.c_str(), overlayName.c_str()); |
| |
| std::string err_result; |
| if (!dlLoader.callCreateIdmapFile(err_result, targetPath.c_str(), overlayPath.c_str(), |
| idmapPath.c_str(), overlayName.c_str(), |
| (isSystem == JNI_TRUE), (isVendor == JNI_TRUE), |
| (isProduct == JNI_TRUE), (isTargetSignature == JNI_TRUE), |
| (isOdm == JNI_TRUE), (isOem == JNI_TRUE))) { |
| jniThrowException(env, kIOException, err_result.c_str()); |
| return; |
| } |
| } |
| |
| static jobject GetFabricatedOverlayInfo(JNIEnv* env, jclass /* clazz */, jstring jsOverlayPath) { |
| const ScopedUtfChars overlay_path(env, jsOverlayPath); |
| if (overlay_path.c_str() == nullptr) { |
| jniThrowNullPointerException(env, "overlay_path is null"); |
| return nullptr; |
| } |
| ALOGV("overlay_path = %s", overlay_path.c_str()); |
| |
| DynamicLibraryLoader& dlLoader = EnsureDynamicLibraryLoader(env); |
| if (!dlLoader) { |
| return nullptr; |
| } |
| |
| std::string err_result; |
| OverlayManifestInfo overlay_manifest_info; |
| if (!dlLoader.callGetFabricatedOverlayInfo(err_result, overlay_path.c_str(), |
| overlay_manifest_info) != 0) { |
| jniThrowException(env, kIOException, err_result.c_str()); |
| return nullptr; |
| } |
| jobject info = env->NewObject(gFabricatedOverlayInfoOffsets.classObject, |
| gFabricatedOverlayInfoOffsets.constructor); |
| jstring jsOverName = env->NewStringUTF(overlay_manifest_info.name.c_str()); |
| jstring jsPackageName = env->NewStringUTF(overlay_manifest_info.package_name.c_str()); |
| jstring jsTargetPackage = env->NewStringUTF(overlay_manifest_info.target_package.c_str()); |
| jstring jsTargetOverlayable = env->NewStringUTF(overlay_manifest_info.target_name.c_str()); |
| env->SetObjectField(info, gFabricatedOverlayInfoOffsets.overlayName, jsOverName); |
| env->SetObjectField(info, gFabricatedOverlayInfoOffsets.packageName, jsPackageName); |
| env->SetObjectField(info, gFabricatedOverlayInfoOffsets.targetPackageName, jsTargetPackage); |
| env->SetObjectField(info, gFabricatedOverlayInfoOffsets.targetOverlayable, jsTargetOverlayable); |
| env->SetObjectField(info, gFabricatedOverlayInfoOffsets.path, jsOverlayPath); |
| return info; |
| } |
| |
| } // namespace self_targeting |
| |
| // JNI registration. |
| static const JNINativeMethod gOverlayManagerMethods[] = { |
| {"createFrroFile", "(Ljava/lang/String;Landroid/os/FabricatedOverlayInternal;)V", |
| reinterpret_cast<void*>(self_targeting::CreateFrroFile)}, |
| {"createIdmapFile", |
| "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZZZZ)V", |
| reinterpret_cast<void*>(self_targeting::CreateIdmapFile)}, |
| {"getFabricatedOverlayInfo", "(Ljava/lang/String;)Landroid/os/FabricatedOverlayInfo;", |
| reinterpret_cast<void*>(self_targeting::GetFabricatedOverlayInfo)}, |
| }; |
| |
| int register_com_android_internal_content_om_OverlayManagerImpl(JNIEnv* env) { |
| jclass ListClass = FindClassOrDie(env, "java/util/List"); |
| gListOffsets.classObject = MakeGlobalRefOrDie(env, ListClass); |
| gListOffsets.size = GetMethodIDOrDie(env, gListOffsets.classObject, "size", "()I"); |
| gListOffsets.get = |
| GetMethodIDOrDie(env, gListOffsets.classObject, "get", "(I)Ljava/lang/Object;"); |
| |
| jclass fabricatedOverlayInternalClass = |
| FindClassOrDie(env, "android/os/FabricatedOverlayInternal"); |
| gFabricatedOverlayInternalOffsets.classObject = |
| MakeGlobalRefOrDie(env, fabricatedOverlayInternalClass); |
| gFabricatedOverlayInternalOffsets.packageName = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalOffsets.classObject, "packageName", |
| "Ljava/lang/String;"); |
| gFabricatedOverlayInternalOffsets.overlayName = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalOffsets.classObject, "overlayName", |
| "Ljava/lang/String;"); |
| gFabricatedOverlayInternalOffsets.targetPackageName = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalOffsets.classObject, "targetPackageName", |
| "Ljava/lang/String;"); |
| gFabricatedOverlayInternalOffsets.targetOverlayable = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalOffsets.classObject, "targetOverlayable", |
| "Ljava/lang/String;"); |
| gFabricatedOverlayInternalOffsets.entries = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalOffsets.classObject, "entries", |
| "Ljava/util/List;"); |
| |
| jclass fabricatedOverlayInternalEntryClass = |
| FindClassOrDie(env, "android/os/FabricatedOverlayInternalEntry"); |
| gFabricatedOverlayInternalEntryOffsets.classObject = |
| MakeGlobalRefOrDie(env, fabricatedOverlayInternalEntryClass); |
| gFabricatedOverlayInternalEntryOffsets.resourceName = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalEntryOffsets.classObject, "resourceName", |
| "Ljava/lang/String;"); |
| gFabricatedOverlayInternalEntryOffsets.dataType = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalEntryOffsets.classObject, "dataType", |
| "I"); |
| gFabricatedOverlayInternalEntryOffsets.data = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalEntryOffsets.classObject, "data", "I"); |
| gFabricatedOverlayInternalEntryOffsets.stringData = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalEntryOffsets.classObject, "stringData", |
| "Ljava/lang/String;"); |
| gFabricatedOverlayInternalEntryOffsets.binaryData = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalEntryOffsets.classObject, "binaryData", |
| "Landroid/os/ParcelFileDescriptor;"); |
| gFabricatedOverlayInternalEntryOffsets.configuration = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalEntryOffsets.classObject, |
| "configuration", "Ljava/lang/String;"); |
| gFabricatedOverlayInternalEntryOffsets.binaryDataOffset = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalEntryOffsets.classObject, |
| "binaryDataOffset", "J"); |
| gFabricatedOverlayInternalEntryOffsets.binaryDataSize = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalEntryOffsets.classObject, |
| "binaryDataSize", "J"); |
| gFabricatedOverlayInternalEntryOffsets.isNinePatch = |
| GetFieldIDOrDie(env, gFabricatedOverlayInternalEntryOffsets.classObject, "isNinePatch", |
| "Z"); |
| |
| jclass parcelFileDescriptorClass = |
| android::FindClassOrDie(env, "android/os/ParcelFileDescriptor"); |
| gParcelFileDescriptorOffsets.classObject = MakeGlobalRefOrDie(env, parcelFileDescriptorClass); |
| gParcelFileDescriptorOffsets.getFd = |
| GetMethodIDOrDie(env, gParcelFileDescriptorOffsets.classObject, "getFd", "()I"); |
| |
| jclass fabricatedOverlayInfoClass = FindClassOrDie(env, "android/os/FabricatedOverlayInfo"); |
| gFabricatedOverlayInfoOffsets.classObject = MakeGlobalRefOrDie(env, fabricatedOverlayInfoClass); |
| gFabricatedOverlayInfoOffsets.constructor = |
| GetMethodIDOrDie(env, gFabricatedOverlayInfoOffsets.classObject, "<init>", "()V"); |
| gFabricatedOverlayInfoOffsets.packageName = |
| GetFieldIDOrDie(env, gFabricatedOverlayInfoOffsets.classObject, "packageName", |
| "Ljava/lang/String;"); |
| gFabricatedOverlayInfoOffsets.overlayName = |
| GetFieldIDOrDie(env, gFabricatedOverlayInfoOffsets.classObject, "overlayName", |
| "Ljava/lang/String;"); |
| gFabricatedOverlayInfoOffsets.targetPackageName = |
| GetFieldIDOrDie(env, gFabricatedOverlayInfoOffsets.classObject, "targetPackageName", |
| "Ljava/lang/String;"); |
| gFabricatedOverlayInfoOffsets.targetOverlayable = |
| GetFieldIDOrDie(env, gFabricatedOverlayInfoOffsets.classObject, "targetOverlayable", |
| "Ljava/lang/String;"); |
| gFabricatedOverlayInfoOffsets.path = |
| GetFieldIDOrDie(env, gFabricatedOverlayInfoOffsets.classObject, "path", |
| "Ljava/lang/String;"); |
| |
| return RegisterMethodsOrDie(env, "com/android/internal/content/om/OverlayManagerImpl", |
| gOverlayManagerMethods, NELEM(gOverlayManagerMethods)); |
| } |
| |
| } // namespace android |