1mod error;
15
16pub use error::*;
17
18pub use jni::{
19    AttachGuard, JNIEnv, JavaVM, NativeMethod,
20    errors::Error as JniError,
21    objects::{
22        GlobalRef, JBooleanArray, JByteArray, JClass, JObject, JObjectArray, JString, JValueGen,
23        ReleaseMode,
24    },
25    sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort, jsize},
26};
27use log::{debug, error, warn};
28use parking_lot::ReentrantMutex;
29use std::{
30    cell::RefCell,
31    collections::HashMap,
32    fmt::Debug,
33    hash::{DefaultHasher, Hash, Hasher},
34    str::FromStr,
35    sync::{Arc, LazyLock, OnceLock},
36};
37
38static HOOK_OBJECTS: LazyLock<
40    ReentrantMutex<
41        RefCell<
42            HashMap<
43                i32,
44                Arc<
45                    dyn Fn(&mut JNIEnv<'_>, &JObject<'_>, &JObjectArray<'_>) -> Result<GlobalRef>
46                        + Send
47                        + Sync,
48                >,
49            >,
50        >,
51    >,
52> = LazyLock::new(|| ReentrantMutex::new(RefCell::new(HashMap::new())));
53static HOOK_OBJECTS_OTHER: LazyLock<ReentrantMutex<RefCell<HashMap<u64, i32>>>> =
55    LazyLock::new(|| ReentrantMutex::new(RefCell::new(HashMap::new())));
56
57#[macro_export]
62macro_rules! import {
63    () => {
64        use std::{
65            rc::Rc,
66            sync::{Arc, Mutex},
67        };
68        pub use $crate::Result;
69        use $crate::{
70            GlobalRef, JObject, impl_array, null_value, to_java_byte_array, to_java_object_array,
71            unbind_proxy_handler, vm_attach,
72        };
73
74        pub trait JObjRef {
79            fn java_ref(&self) -> Result<GlobalRef>;
83        }
84
85        pub trait JObjNew {
89            type Fields: Default;
91
92            fn _new(this: &GlobalRef, fields: Self::Fields) -> Result<Self>
97            where
98                Self: Sized;
99
100            fn null() -> Result<Self>
104            where
105                Self: Sized,
106                Self::Fields: Default,
107            {
108                let mut env = vm_attach()?;
109                Self::_new(null_value(&mut env)?.as_ref(), Default::default())
110            }
111        }
112
113        pub trait JType: JObjRef + JObjNew {
117            const CLASS: &'static str;
119
120            const OBJECT_SIG: &'static str;
122
123            const DIM: u8 = 0;
125        }
126
127        pub trait JProxy: JObjNew + JObjRef {
131            fn new(fields: Self::Fields) -> Result<Arc<Self>>;
136
137            fn release(&self) -> () {
141                if let Ok(ref r) = self.java_ref() {
142                    unbind_proxy_handler(r)
143                }
144            }
145        }
146
147        impl<T: JObjRef> JObjRef for &T {
148            fn java_ref(&self) -> Result<GlobalRef> {
149                self.java_ref()
150            }
151        }
152
153        impl<T: JObjNew> JObjNew for &T {
154            type Fields = T::Fields;
155
156            fn _new(this: &GlobalRef, fields: Self::Fields) -> Result<Self> {
157                panic!("Reference types cannot be constructed.")
158            }
159        }
160
161        impl<T: JObjRef + JObjNew> JObjRef for Option<T>
162        where
163            <T as JObjNew>::Fields: Default,
164        {
165            fn java_ref(&self) -> Result<GlobalRef> {
166                match self {
167                    None => T::null()?.java_ref(),
168                    Some(v) => v.java_ref(),
169                }
170            }
171        }
172
173        impl<T: JObjRef + JObjNew> JObjNew for Option<T> {
174            type Fields = T::Fields;
175
176            fn _new(this: &GlobalRef, fields: Self::Fields) -> Result<Self> {
177                Ok(match this.is_null() {
178                    true => None,
179                    false => T::_new(this, fields).ok(),
180                })
181            }
182        }
183
184        impl<T: JType> JType for Arc<T> {
185            const CLASS: &'static str = T::CLASS;
186            const OBJECT_SIG: &'static str = T::OBJECT_SIG;
187        }
188
189        impl<T: JObjRef> JObjRef for Arc<T> {
190            fn java_ref(&self) -> Result<GlobalRef> {
191                self.as_ref().java_ref()
192            }
193        }
194
195        impl<T: JObjNew> JObjNew for Arc<T> {
196            type Fields = T::Fields;
197
198            fn _new(this: &GlobalRef, fields: Self::Fields) -> Result<Self> {
199                Ok(T::_new(this, fields)?.into())
200            }
201        }
202
203        impl<T: JType> JType for Rc<T> {
204            const CLASS: &'static str = T::CLASS;
205            const OBJECT_SIG: &'static str = T::OBJECT_SIG;
206        }
207
208        impl<T: JObjRef> JObjRef for Rc<T> {
209            fn java_ref(&self) -> Result<GlobalRef> {
210                self.as_ref().java_ref()
211            }
212        }
213
214        impl<T: JObjNew> JObjNew for Rc<T> {
215            type Fields = T::Fields;
216
217            fn _new(this: &GlobalRef, fields: Self::Fields) -> Result<Self> {
218                Ok(T::_new(this, fields)?.into())
219            }
220        }
221
222        impl<T: JType> JType for Mutex<T> {
223            const CLASS: &'static str = T::CLASS;
224            const OBJECT_SIG: &'static str = T::OBJECT_SIG;
225        }
226
227        impl<T: JObjNew> JObjNew for Mutex<T> {
228            type Fields = T::Fields;
229
230            fn _new(this: &GlobalRef, fields: Self::Fields) -> Result<Self> {
231                Ok(T::_new(this, fields)?.into())
232            }
233        }
234
235        impl<T: JObjRef> JObjRef for Mutex<T> {
236            fn java_ref(&self) -> Result<GlobalRef> {
237                self.java_ref()
238            }
239        }
240
241        impl_array!(u8, 1);
242        impl_array!(String, 1);
243    };
244}
245
246#[macro_export]
255macro_rules! impl_array {
256    (String, $dim: expr) => {
257        impl JObjNew for &[String] {
258            type Fields = ();
259
260            fn _new(this: &GlobalRef, fields: Self::Fields) -> Result<Self> {
261                Ok(&[])
262            }
263        }
264
265        impl JObjRef for &[String] {
266            fn java_ref(&self) -> Result<GlobalRef> {
267                let mut env = vm_attach()?;
268                let arr = self
269                    .iter()
270                    .filter_map(|i| env.new_string(i).ok())
271                    .collect::<Vec<_>>();
272                let sig = if Self::DIM <= 1 {
273                    Self::CLASS.to_string()
274                } else {
275                    "[".repeat((Self::DIM - 1) as _) + Self::OBJECT_SIG
276                };
277                let arr = to_java_object_array(&mut env, &arr, &sig)?;
278                Ok(env.new_global_ref(&arr)?)
279            }
280        }
281
282        impl JType for &[String] {
283            const CLASS: &'static str = <String as JType>::CLASS;
284            const OBJECT_SIG: &'static str = <String as JType>::OBJECT_SIG;
285            const DIM: u8 = $dim;
286        }
287
288        impl JType for String {
290            const CLASS: &'static str = "java/lang/String";
291            const OBJECT_SIG: &'static str = "Ljava/lang/String;";
292        }
293
294        impl JObjRef for String {
295            fn java_ref(&self) -> Result<GlobalRef> {
296                let mut env = vm_attach()?;
297                let jstring = env.new_string(self)?;
298                Ok(env.new_global_ref(&jstring)?)
299            }
300        }
301
302        impl JObjNew for String {
303            type Fields = ();
304
305            fn _new(this: &GlobalRef, _fields: Self::Fields) -> Result<Self> {
306                if this.is_null() {
307                    return Ok(String::new());
308                }
309
310                let mut env = vm_attach()?;
311                let jstring = this.as_obj();
312                let java_string = env.get_string(jstring.into())?;
313                Ok(java_string.to_str()?.to_string())
314            }
315        }
316    };
317
318    (u8, $dim:expr) => {
319        impl JObjNew for &[u8] {
320            type Fields = ();
321
322            fn _new(this: &GlobalRef, fields: Self::Fields) -> Result<Self> {
323                Ok(&[])
324            }
325        }
326
327        impl JObjRef for &[u8] {
328            fn java_ref(&self) -> Result<GlobalRef> {
329                let mut env = vm_attach()?;
330                let arr = self.iter().map(|i| *i as _).collect::<Vec<_>>();
331                let arr = to_java_byte_array(&mut env, &arr)?;
332                Ok(env.new_global_ref(&arr)?)
333            }
334        }
335
336        impl JType for &[u8] {
337            const CLASS: &'static str = "B";
338            const OBJECT_SIG: &'static str = "B";
339            const DIM: u8 = $dim;
340        }
341    };
342}
343
344pub fn android_vm<'a>() -> Result<&'static JavaVM> {
348    static JAVA_VM: LazyLock<Result<JavaVM>> = LazyLock::new(|| {
349        let ctx = ndk_context::android_context();
350        let vm = unsafe { JavaVM::from_raw(ctx.vm().cast()) }?;
351        Ok(vm)
352    });
353    JAVA_VM.as_ref().map_err(|e| e.to_owned())
354}
355
356#[inline(always)]
369pub fn vm_attach<'a>() -> Result<AttachGuard<'a>> {
370    let vm = android_vm()?;
371    vm.attach_current_thread().map_err(|e| e.into())
372}
373
374pub fn android_context<'a>() -> JObject<'a> {
378    let ctx = ndk_context::android_context();
379    unsafe { JObject::from_raw(ctx.context().cast()) }
380}
381
382
383pub fn load_class<'a>(class_name: &str) -> Result<JClass<'a>> {
387    let mut env = vm_attach()?;
388    let activity = android_context();
389    let class_loader = env.call_method(
390        activity,
391        "getClassLoader",
392        "()Ljava/lang/ClassLoader;",
393        &[],
394    )?;
395    let cls_name = env.new_string(class_name)?;
396    let loaded_class = env.call_method(
397        class_loader.l()?,
398        "loadClass",
399        "(Ljava/lang/String;)Ljava/lang/Class;",
400        &[(&cls_name).into()],
401    )?;
402    Ok(loaded_class.l()?.into())
403}
404
405pub fn new_proxy(interfaces: &[&str]) -> Result<GlobalRef> {
422    let class = load_rust_call_method_hook_class()?;
423    let mut env = vm_attach()?;
424    let obj = env.new_object(class, "()V", &[])?;
425    let faces = env.new_object_array(
426        interfaces.len() as jsize,
427        "java/lang/Class",
428        &JObject::null(),
429    )?;
430    for i in 0..interfaces.len() {
431        let class = env.new_string(interfaces[i])?;
432        let face = env
433            .call_static_method(
434                "java/lang/Class",
435                "forName",
436                "(Ljava/lang/String;)Ljava/lang/Class;",
437                &[(&class).into()],
438            )?
439            .l()?;
440        env.set_object_array_element(&faces, i as jsize, &face)?;
441    }
442    let hash_code = env.call_method(&obj, "hashCode", "()I", &[])?.i()?;
443    let res = env.call_static_method(
444        "java/lang/reflect/Proxy",
445        "newProxyInstance",
446        "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;",
447        &[
448            (&JObject::null()).into(),
449            (&faces).into(),
450            (&obj).into()
451        ]
452    )?
453        .l()?;
454    let res = env.new_global_ref(&res)?;
455    let lock = HOOK_OBJECTS_OTHER.lock();
456    let mut hasher = DefaultHasher::new();
457    res.hash(&mut hasher);
458    lock.borrow_mut().insert(hasher.finish(), hash_code);
459    drop(lock);
460    Ok(res)
461}
462
463pub fn bind_proxy_handler(
488    proxy: &GlobalRef,
489    handler: impl Fn(&mut JNIEnv<'_>, &JObject<'_>, &JObjectArray<'_>) -> Result<GlobalRef>
490    + Send
491    + Sync
492    + 'static,
493) {
494    let hash_code = get_proxy_hash_code(proxy);
495    let lock = match HOOK_OBJECTS.try_lock() {
496        Some(lock) => lock,
497        None => {
498            error!("Can't bind proxy handler.");
499            return;
500        }
501    };
502    lock.borrow_mut().insert(hash_code, Arc::new(handler));
503}
504
505pub fn get_proxy_hash_code(proxy: &GlobalRef) -> i32 {
521    let mut hasher = DefaultHasher::new();
522    proxy.hash(&mut hasher);
523    let proxy_hash_code = hasher.finish();
524    let lock = HOOK_OBJECTS_OTHER.lock();
525    if let Some(code) = lock.borrow().get(&proxy_hash_code) {
526        return *code;
527    };
528    0
529}
530
531pub fn unbind_proxy_handler(proxy: &GlobalRef) {
548    let mut hasher = DefaultHasher::new();
549    proxy.hash(&mut hasher);
550    let proxy_hash_code = hasher.finish();
551    let lock = HOOK_OBJECTS_OTHER.lock();
552    if let Some(code) = lock.borrow().get(&proxy_hash_code) {
553        let lock = HOOK_OBJECTS.lock();
554        lock.borrow_mut().remove_entry(code);
555        debug!("Proxy `{}` is dropped.", proxy_hash_code);
556    };
557}
558
559pub trait ParseJObjectType<T: FromStr> {
563    fn parse(&self, env: &mut JNIEnv) -> Result<T>
564    where
565        <T as FromStr>::Err: Debug;
566}
567
568impl<T: FromStr> ParseJObjectType<T> for JObject<'_> {
569    fn parse(&self, env: &mut JNIEnv) -> Result<T>
571    where
572        <T as FromStr>::Err: Debug,
573    {
574        let s = env
575            .call_method(self, "toString", "()Ljava/lang/String;", &[])?
576            .l()?;
577        let s = env.get_string((&s).into())?;
578        let s = s.to_str()?;
579        Ok(s.parse()
580            .map_err(|_| DroidWrapError::FromStr(format!("Invalid value: {}", s)))?)
581    }
582}
583
584fn load_rust_call_method_hook_class<'a>() -> Result<&'a GlobalRef> {
586    #[cfg(target_os = "android")]
587    const BYTECODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/classes.dex"));
588    #[cfg(not(target_os = "android"))]
589    const BYTECODE: &[u8] = &[];
590    const LOADER_CLASS: &str = "dalvik/system/InMemoryDexClassLoader";
591    static INSTANCE: OnceLock<Result<GlobalRef>> = OnceLock::new();
592
593    INSTANCE.get_or_init(|| {
594        let mut env = vm_attach()?;
595        let byte_buffer = unsafe { env.new_direct_byte_buffer(BYTECODE.as_ptr() as *mut u8, BYTECODE.len()) }?;
596
597        let dex_class_loader = env
598            .new_object(
599                LOADER_CLASS,
600                "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V",
601                &[
602                    JValueGen::Object(&JObject::from(byte_buffer)),
603                    JValueGen::Object(&JObject::null()),
604                ],
605            )?;
606
607        let class = env.new_string("rust/CallMethodHook")?;
608        let class = env
609            .call_method(
610                &dex_class_loader,
611                "loadClass",
612                "(Ljava/lang/String;)Ljava/lang/Class;",
613                &[(&class).into()],
614            )?
615            .l()?;
616        let m = NativeMethod {
617            name: "invoke".into(),
618            sig: "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;".into(),
619            fn_ptr: rust_callback as *mut _,
620        };
621        env.register_native_methods(Into::<&JClass<'_>>::into(&class), &[m])?;
622
623        Ok(env.new_global_ref(&class)?)
624    }).as_ref().map_err(|e| e.clone())
625}
626
627unsafe extern "C" fn rust_callback<'a>(
629    mut env: JNIEnv<'a>,
630    this: JObject<'a>,
631    _: JObject<'a>,
632    method: JObject<'a>,
633    args: JObjectArray<'a>,
634) -> JObject<'a> {
635    fn get_hash_code<'a>(env: &mut JNIEnv<'a>, this: &JObject<'a>) -> Result<i32> {
636        Ok::<_, DroidWrapError>(env.call_method(this, "hashCode", "()I", &[])?.i()?)
637    }
638
639    fn get_name<'a>(env: &mut JNIEnv<'a>, method: &JObject<'a>) -> Result<String> {
640        let name = env
641            .call_method(method, "getName", "()Ljava/lang/String;", &[])?
642            .l()?;
643        Ok::<_, DroidWrapError>(env.get_string((&name).into())?.to_str()?.to_string())
644    }
645
646    let hash_code = get_hash_code(&mut env, &this).unwrap_or_default();
647
648    match get_name(&mut env, &method).unwrap_or_default().as_str() {
649        "toString" => {
650            return match env.new_string(format!("Proxy@{:x}", hash_code).as_str()) {
651                Ok(r) => r.into(),
652                _ => JObject::null(),
653            };
654        }
655        "equals" | "hashCode" => {
656            return match env.call_method(
657                &method,
658                "invoke",
659                "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
660                &[(&this).into(), (&args).into()],
661            ) {
662                Ok(r) => match r.l() {
663                    Ok(r) => r,
664                    Err(e) => {
665                        error!("{}", e);
666                        return JObject::null();
667                    }
668                },
669                Err(e) => {
670                    error!("{}", e);
671                    return JObject::null();
672                }
673            };
674        }
675        _ => (),
676    }
677
678    let lock = HOOK_OBJECTS.lock();
679    let func = if let Some(f) = lock.borrow().get(&hash_code) {
680        f.to_owned()
681    } else {
682        warn!(
683            "The method call has reached, but it appears that the proxy object has been dropped."
684        );
685        return JObject::null();
686    };
687    drop(lock);
688
689    match func(&mut env, &method, &args) {
690        Ok(ret) => match env.new_local_ref(ret.as_obj()) {
691            Ok(r) => return r,
692            Err(e) => {
693                error!("{}", e);
694                JObject::null()
695            }
696        },
697        Err(e) => {
698            error!("{}", e);
699            JObject::null()
700        }
701    }
702}
703
704pub fn to_vec<'a>(env: &mut JNIEnv<'a>, arr: &JObjectArray) -> Result<Vec<JObject<'a>>> {
727    let size = env.get_array_length(arr)?;
728    let mut arr2 = Vec::with_capacity(size as usize);
729    for i in 0..size {
730        arr2.push(env.get_object_array_element(arr, i)?);
731    }
732
733    Ok(arr2)
734}
735
736pub fn to_java_object_array<'a, O: AsRef<JObject<'a>>>(
764    env: &mut JNIEnv<'a>,
766    arr: &[O],
768    element_class: &str,
770) -> Result<JObjectArray<'a>> {
771    let arr2 = env.new_object_array(arr.len() as _, element_class, JObject::null())?;
773    for (i, j) in arr.iter().enumerate() {
775        env.set_object_array_element(&arr2, i as _, j)?;
776    }
777
778    Ok(arr2)
780}
781
782pub fn to_java_byte_array<'a>(
810    env: &mut JNIEnv<'a>,
812    arr: &[jbyte],
814) -> Result<JByteArray<'a>> {
815    let arr2 = env.new_byte_array(arr.len() as _)?;
817    env.set_byte_array_region(&arr2, 0, arr)?;
819    Ok(arr2)
821}
822
823pub fn null_value(env: &mut JNIEnv) -> Result<GlobalRef> {
841    let obj = JObject::null();
842    Ok(env.new_global_ref(&obj)?)
843}
844
845pub fn wrapper_bool_value(value: bool, env: &mut JNIEnv) -> Result<GlobalRef> {
864    let obj = env.new_object("java/lang/Boolean", "(Z)V", &[(value as jboolean).into()])?;
865    Ok(env.new_global_ref(&obj)?)
866}
867
868pub fn wrapper_integer_value(value: i32, env: &mut JNIEnv) -> Result<GlobalRef> {
887    let obj = env.new_object("java/lang/Integer", "(I)V", &[value.into()])?;
888    Ok(env.new_global_ref(&obj)?)
889}
890
891pub fn wrapper_long_value(value: i64, env: &mut JNIEnv) -> Result<GlobalRef> {
910    let obj = env.new_object("java/lang/Long", "(J)V", &[value.into()])?;
911    Ok(env.new_global_ref(&obj)?)
912}
913
914pub fn java_object_equals<'a, O: AsRef<JObject<'a>>>(a: O, b: O) -> Result<bool> {
936    let a = a.as_ref();
937    let b = b.as_ref();
938    if a.is_null() && a.is_null() {
939        return Ok(true);
940    }
941    if a.is_null() {
942        return Ok(false);
943    }
944    let mut env = vm_attach()?;
945    let res = env
946        .call_method(a, "equals", "(Ljava/lang/Object;)Z", &[b.into()])?
947        .z()?;
948
949    Ok(res)
950}
951
952pub fn java_object_to_string<'a, O: AsRef<JObject<'a>>>(obj: O) -> Result<String> {
972    let obj = obj.as_ref();
973    if obj.is_null() {
974        return Err(DroidWrapError::Jni(JniError::NullPtr("to_string")));
975    }
976    let mut env = vm_attach()?;
977    let s = env
978        .call_method(obj, "toString", "()Ljava/lang/String;", &[])?
979        .l()?;
980    let s = env.get_string((&s).into())?;
981
982    Ok(s.to_str()?.to_string())
983}