manganis: load FFI classes from the activity classloader, not FindClass

JNIEnv::find_class resolves against the system classloader on a thread
attached from native code, which cannot see APK classes (#5629). Route
the generated #[manganis::ffi] type lookup through
load_class_from_classloader, and reimplement that helper to ask the
activity's own ClassLoader.loadClass instead of Class.forName.
This commit is contained in:
swaits committed 2026-09-05 20:34:33 -06:00
1 parent 07dddd569e
commit aeb5568727
2 files changed
+25 -6

No files matched your search

+1 -1
View File
@@ -398,7 +398,7 @@ impl FfiBridgeParser {
// Use with_activity which returns Option<R>, wrapping our Result in the Option
let inner_result: Option<Result<Self, String>> = manganis::android::with_activity(|mut env, activity| {
// Find the class
let class_result = env.find_class(#class_name_lit);
let class_result = manganis::android::load_class_from_classloader(env, activity, #class_name_lit);
let class = match class_result {
Ok(c) => c,
Err(e) => return Some(Err(format!("Failed to find class {}: {:?}", #class_name_lit, e))),
+24 -5
View File
@@ -111,15 +111,34 @@ pub fn request_permissions_via_helper(
Ok(())
}
/// Load a Java class from the APK's classloader
/// Load a Java class from the APK's classloader.
///
/// `JNIEnv::find_class` resolves against the *system* classloader on a thread that
/// was attached from native code (`JavaVM::attach_current_thread`), which cannot see
/// classes packaged in the APK, so it raises `ClassNotFoundException`. The activity's
/// own classloader can, so we ask the activity for it and call `loadClass` on it.
///
/// `class_name` may be given in either JNI form (`com/example/Foo`) or binary form
/// (`com.example.Foo`); `loadClass` requires the binary form, so slashes are converted.
pub fn load_class_from_classloader<'env>(
env: &mut JNIEnv<'env>,
activity: &JObject<'_>,
class_name: &str,
) -> Result<JClass<'env>> {
let class_name_jstring = new_string(env, class_name)?;
let class = env.call_static_method(
"java/lang/Class",
"forName",
let binary_name = class_name.replace('/', ".");
let activity_class = env.get_object_class(activity)?;
let loader = env
.call_method(
&activity_class,
"getClassLoader",
"()Ljava/lang/ClassLoader;",
&[],
)?
.l()?;
let class_name_jstring = new_string(env, &binary_name)?;
let class = env.call_method(
&loader,
"loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;",
&[JValue::Object(&class_name_jstring)],
)?;