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}