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
289 (u8, $dim:expr) => {
290 impl JObjNew for &[u8] {
291 type Fields = ();
292
293 fn _new(this: &GlobalRef, fields: Self::Fields) -> Result<Self> {
294 Ok(&[])
295 }
296 }
297
298 impl JObjRef for &[u8] {
299 fn java_ref(&self) -> Result<GlobalRef> {
300 let mut env = vm_attach()?;
301 let arr = self.iter().map(|i| *i as _).collect::<Vec<_>>();
302 let arr = to_java_byte_array(&mut env, &arr)?;
303 Ok(env.new_global_ref(&arr)?)
304 }
305 }
306
307 impl JType for &[u8] {
308 const CLASS: &'static str = "B";
309 const OBJECT_SIG: &'static str = "B";
310 const DIM: u8 = $dim;
311 }
312 };
313}
314
315pub fn android_vm<'a>() -> Result<&'static JavaVM> {
319 static JAVA_VM: LazyLock<Result<JavaVM>> = LazyLock::new(|| {
320 let ctx = ndk_context::android_context();
321 let vm = unsafe { JavaVM::from_raw(ctx.vm().cast()) }?;
322 Ok(vm)
323 });
324 JAVA_VM.as_ref().map_err(|e| e.to_owned())
325}
326
327#[inline(always)]
340pub fn vm_attach<'a>() -> Result<AttachGuard<'a>> {
341 let vm = android_vm()?;
342 vm.attach_current_thread().map_err(|e| e.into())
343}
344
345pub fn android_context<'a>() -> JObject<'a> {
349 let ctx = ndk_context::android_context();
350 unsafe { JObject::from_raw(ctx.context().cast()) }
351}
352
353pub fn new_proxy(interfaces: &[&str]) -> Result<GlobalRef> {
370 let class = load_rust_call_method_hook_class()?;
371 let mut env = vm_attach()?;
372 let obj = env.new_object(class, "()V", &[])?;
373 let faces = env.new_object_array(
374 interfaces.len() as jsize,
375 "java/lang/Class",
376 &JObject::null(),
377 )?;
378 for i in 0..interfaces.len() {
379 let class = env.new_string(interfaces[i])?;
380 let face = env
381 .call_static_method(
382 "java/lang/Class",
383 "forName",
384 "(Ljava/lang/String;)Ljava/lang/Class;",
385 &[(&class).into()],
386 )?
387 .l()?;
388 env.set_object_array_element(&faces, i as jsize, &face)?;
389 }
390 let hash_code = env.call_method(&obj, "hashCode", "()I", &[])?.i()?;
391 let res = env.call_static_method(
392 "java/lang/reflect/Proxy",
393 "newProxyInstance",
394 "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;",
395 &[
396 (&JObject::null()).into(),
397 (&faces).into(),
398 (&obj).into()
399 ]
400 )?
401 .l()?;
402 let res = env.new_global_ref(&res)?;
403 let lock = HOOK_OBJECTS_OTHER.lock();
404 let mut hasher = DefaultHasher::new();
405 res.hash(&mut hasher);
406 lock.borrow_mut().insert(hasher.finish(), hash_code);
407 drop(lock);
408 Ok(res)
409}
410
411pub fn bind_proxy_handler(
436 proxy: &GlobalRef,
437 handler: impl Fn(&mut JNIEnv<'_>, &JObject<'_>, &JObjectArray<'_>) -> Result<GlobalRef>
438 + Send
439 + Sync
440 + 'static,
441) {
442 let hash_code = get_proxy_hash_code(proxy);
443 let lock = match HOOK_OBJECTS.try_lock() {
444 Some(lock) => lock,
445 None => {
446 error!("Can't bind proxy handler.");
447 return;
448 }
449 };
450 lock.borrow_mut().insert(hash_code, Arc::new(handler));
451}
452
453pub fn get_proxy_hash_code(proxy: &GlobalRef) -> i32 {
469 let mut hasher = DefaultHasher::new();
470 proxy.hash(&mut hasher);
471 let proxy_hash_code = hasher.finish();
472 let lock = HOOK_OBJECTS_OTHER.lock();
473 if let Some(code) = lock.borrow().get(&proxy_hash_code) {
474 return *code;
475 };
476 0
477}
478
479pub fn unbind_proxy_handler(proxy: &GlobalRef) {
496 let mut hasher = DefaultHasher::new();
497 proxy.hash(&mut hasher);
498 let proxy_hash_code = hasher.finish();
499 let lock = HOOK_OBJECTS_OTHER.lock();
500 if let Some(code) = lock.borrow().get(&proxy_hash_code) {
501 let lock = HOOK_OBJECTS.lock();
502 lock.borrow_mut().remove_entry(code);
503 debug!("Proxy `{}` is dropped.", proxy_hash_code);
504 };
505}
506
507pub trait ParseJObjectType<T: FromStr> {
511 fn parse(&self, env: &mut JNIEnv) -> Result<T>
512 where
513 <T as FromStr>::Err: Debug;
514}
515
516impl<T: FromStr> ParseJObjectType<T> for JObject<'_> {
517 fn parse(&self, env: &mut JNIEnv) -> Result<T>
519 where
520 <T as FromStr>::Err: Debug,
521 {
522 let s = env
523 .call_method(self, "toString", "()Ljava/lang/String;", &[])?
524 .l()?;
525 let s = env.get_string((&s).into())?;
526 let s = s.to_str()?;
527 Ok(s.parse()
528 .map_err(|_| DroidWrapError::FromStr(format!("Invalid value: {}", s)))?)
529 }
530}
531
532fn load_rust_call_method_hook_class<'a>() -> Result<&'a GlobalRef> {
534 #[cfg(target_os = "android")]
535 const BYTECODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/classes.dex"));
536 #[cfg(not(target_os = "android"))]
537 const BYTECODE: &[u8] = &[];
538 const LOADER_CLASS: &str = "dalvik/system/InMemoryDexClassLoader";
539 static INSTANCE: OnceLock<Result<GlobalRef>> = OnceLock::new();
540
541 INSTANCE.get_or_init(|| {
542 let mut env = vm_attach()?;
543 let byte_buffer = unsafe { env.new_direct_byte_buffer(BYTECODE.as_ptr() as *mut u8, BYTECODE.len()) }?;
544
545 let dex_class_loader = env
546 .new_object(
547 LOADER_CLASS,
548 "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V",
549 &[
550 JValueGen::Object(&JObject::from(byte_buffer)),
551 JValueGen::Object(&JObject::null()),
552 ],
553 )?;
554
555 let class = env.new_string("rust/CallMethodHook")?;
556 let class = env
557 .call_method(
558 &dex_class_loader,
559 "loadClass",
560 "(Ljava/lang/String;)Ljava/lang/Class;",
561 &[(&class).into()],
562 )?
563 .l()?;
564 let m = NativeMethod {
565 name: "invoke".into(),
566 sig: "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;".into(),
567 fn_ptr: rust_callback as *mut _,
568 };
569 env.register_native_methods(Into::<&JClass<'_>>::into(&class), &[m])?;
570
571 Ok(env.new_global_ref(&class)?)
572 }).as_ref().map_err(|e| e.clone())
573}
574
575unsafe extern "C" fn rust_callback<'a>(
577 mut env: JNIEnv<'a>,
578 this: JObject<'a>,
579 _: JObject<'a>,
580 method: JObject<'a>,
581 args: JObjectArray<'a>,
582) -> JObject<'a> {
583 fn get_hash_code<'a>(env: &mut JNIEnv<'a>, this: &JObject<'a>) -> Result<i32> {
584 Ok::<_, DroidWrapError>(env.call_method(this, "hashCode", "()I", &[])?.i()?)
585 }
586
587 fn get_name<'a>(env: &mut JNIEnv<'a>, method: &JObject<'a>) -> Result<String> {
588 let name = env
589 .call_method(method, "getName", "()Ljava/lang/String;", &[])?
590 .l()?;
591 Ok::<_, DroidWrapError>(env.get_string((&name).into())?.to_str()?.to_string())
592 }
593
594 let hash_code = get_hash_code(&mut env, &this).unwrap_or_default();
595
596 match get_name(&mut env, &method).unwrap_or_default().as_str() {
597 "toString" => {
598 return match env.new_string(format!("Proxy@{:x}", hash_code).as_str()) {
599 Ok(r) => r.into(),
600 _ => JObject::null(),
601 };
602 }
603 "equals" | "hashCode" => {
604 return match env.call_method(
605 &method,
606 "invoke",
607 "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
608 &[(&this).into(), (&args).into()],
609 ) {
610 Ok(r) => match r.l() {
611 Ok(r) => r,
612 Err(e) => {
613 error!("{}", e);
614 return JObject::null();
615 }
616 },
617 Err(e) => {
618 error!("{}", e);
619 return JObject::null();
620 }
621 };
622 }
623 _ => (),
624 }
625
626 let lock = HOOK_OBJECTS.lock();
627 let func = if let Some(f) = lock.borrow().get(&hash_code) {
628 f.to_owned()
629 } else {
630 warn!(
631 "The method call has reached, but it appears that the proxy object has been dropped."
632 );
633 return JObject::null();
634 };
635 drop(lock);
636
637 match func(&mut env, &method, &args) {
638 Ok(ret) => match env.new_local_ref(ret.as_obj()) {
639 Ok(r) => return r,
640 Err(e) => {
641 error!("{}", e);
642 JObject::null()
643 }
644 },
645 Err(e) => {
646 error!("{}", e);
647 JObject::null()
648 }
649 }
650}
651
652pub fn to_vec<'a>(env: &mut JNIEnv<'a>, arr: &JObjectArray) -> Result<Vec<JObject<'a>>> {
675 let size = env.get_array_length(arr)?;
676 let mut arr2 = Vec::with_capacity(size as usize);
677 for i in 0..size {
678 arr2.push(env.get_object_array_element(arr, i)?);
679 }
680
681 Ok(arr2)
682}
683
684pub fn to_java_object_array<'a, O: AsRef<JObject<'a>>>(
712 env: &mut JNIEnv<'a>,
714 arr: &[O],
716 element_class: &str,
718) -> Result<JObjectArray<'a>> {
719 let arr2 = env.new_object_array(arr.len() as _, element_class, JObject::null())?;
721 for (i, j) in arr.iter().enumerate() {
723 env.set_object_array_element(&arr2, i as _, j)?;
724 }
725
726 Ok(arr2)
728}
729
730pub fn to_java_byte_array<'a>(
758 env: &mut JNIEnv<'a>,
760 arr: &[jbyte],
762) -> Result<JByteArray<'a>> {
763 let arr2 = env.new_byte_array(arr.len() as _)?;
765 env.set_byte_array_region(&arr2, 0, arr)?;
767 Ok(arr2)
769}
770
771pub fn null_value(env: &mut JNIEnv) -> Result<GlobalRef> {
789 let obj = JObject::null();
790 Ok(env.new_global_ref(&obj)?)
791}
792
793pub fn wrapper_bool_value(value: bool, env: &mut JNIEnv) -> Result<GlobalRef> {
812 let obj = env.new_object("java/lang/Boolean", "(Z)V", &[(value as jboolean).into()])?;
813 Ok(env.new_global_ref(&obj)?)
814}
815
816pub fn wrapper_integer_value(value: i32, env: &mut JNIEnv) -> Result<GlobalRef> {
835 let obj = env.new_object("java/lang/Integer", "(I)V", &[value.into()])?;
836 Ok(env.new_global_ref(&obj)?)
837}
838
839pub fn wrapper_long_value(value: i64, env: &mut JNIEnv) -> Result<GlobalRef> {
858 let obj = env.new_object("java/lang/Long", "(J)V", &[value.into()])?;
859 Ok(env.new_global_ref(&obj)?)
860}
861
862pub fn java_object_equals<'a, O: AsRef<JObject<'a>>>(a: O, b: O) -> Result<bool> {
884 let a = a.as_ref();
885 let b = b.as_ref();
886 if a.is_null() && a.is_null() {
887 return Ok(true);
888 }
889 if a.is_null() {
890 return Ok(false);
891 }
892 let mut env = vm_attach()?;
893 let res = env
894 .call_method(a, "equals", "(Ljava/lang/Object;)Z", &[b.into()])?
895 .z()?;
896
897 Ok(res)
898}
899
900pub fn java_object_to_string<'a, O: AsRef<JObject<'a>>>(obj: O) -> Result<String> {
920 let obj = obj.as_ref();
921 if obj.is_null() {
922 return Err(DroidWrapError::Jni(JniError::NullPtr("to_string")));
923 }
924 let mut env = vm_attach()?;
925 let s = env
926 .call_method(obj, "toString", "()Ljava/lang/String;", &[])?
927 .l()?;
928 let s = env.get_string((&s).into())?;
929
930 Ok(s.to_str()?.to_string())
931}