How to use JNI to call Android Java APIs from Rust?

The jni crate has an example on its doc home page of how to implement a function, in Rust, that you would call from Java or Kotlin. But I want to go the other way. Most of my app is in Rust, but I want to call certain Java APIs, because (as far as I know) there is no Rust API for certain Android feature. For example, I want to call getSharedPreferences on the activity/context, and then store simple data in it.

I create some setup/boilerplate, getting the Android activity, and JNIEnv

fn foo(...) {
    let activity = native_activity();
    let vm = activity.vm();
    let vm = unsafe { JavaVM::from_raw(vm).unwrap() };
    let mut env: AttachGuard = vm.attach_current_thread().unwrap();
    let context = unsafe { JObject::from_raw(activity.activity()) };
    self.get_shared_preferences(&mut *env, context, FILE_KEY)
}

fn get_shared_preferences(&self, env: &mut JNIEnv, context: JObject, file_key: &str) -> JObject {
    let file_key_jstring = env.new_string(file_key).unwrap();

    // Call getSharedPreferences(String name, int mode)
    env.call_method(
        context,
        "getSharedPreferences",
        "(Ljava/lang/String;I)Landroid/content/SharedPreferences;",
        &[
            JValue::Object(&file_key_jstring),
            JValue::Int(0), // Context.MODE_PRIVATE = 0
        ],
    )
        .unwrap()
        .l()
        .unwrap()

The error is:

error: lifetime may not live long enough
   --> settings/src/android.rs:99:9
    |
95  |       fn get_shared_preferences(&self, env: &mut JNIEnv, context: JObject, file_key: &str) -> JObject {
    |                                 -      --- has type `&mut JNIEnv<'1>`
    |                                 |
    |                                 let's call the lifetime of this reference `'2`
...
99  | /         env.call_method(
100 | |             context,
101 | |             "getSharedPreferences",
102 | |             "(Ljava/lang/String;I)Landroid/content/SharedPreferences;",
...   |
109 | |             .l()
110 | |             .unwrap()
    | |_____________________^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
    |
help: consider introducing a named lifetime parameter and update trait if needed
    |
95  |     fn get_shared_preferences<'a>(&self, env: &mut JNIEnv<'a>, context: JObject, file_key: &str) -> JObject<'a> {

When I took advice at the end there, it did not work, but let to another error:

error[E0515]: cannot return value referencing local variable `vm`
  --> settings/src/android.rs:92:9
   |
87 |         let mut env: AttachGuard = vm.attach_current_thread().unwrap();
   |                                    -- `vm` is borrowed here
...
92 |         self.get_shared_preferences(&mut *env, context, FILE_KEY)
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

I'm lost here. I don't understand the meaning of these errors.

both the JNIEnv and JObject has a lifetime parameter, they should match one another, try this:

fn get_shared_preferences<'a>(
    &self,
    env: &mut JNIEnv<'a>,
    context: JObject<'a>,
   file_key: &str
) -> JObject<'a>;

because this function has a complex type signature with multiple lifetimes, the lifetime elision is wrong, you need to explicitly annotate it.