1pub use jni::{
15 errors::Error,
16 objects::{
17 GlobalRef, JBooleanArray, JByteArray, JClass, JObject, JObjectArray, JString, JValueGen,
18 ReleaseMode,
19 },
20 sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort, jsize},
21 AttachGuard, JNIEnv, JavaVM, NativeMethod,
22};
23use log::{debug, error, warn};
24use parking_lot::ReentrantMutex;
25use std::{
26 cell::RefCell,
27 collections::HashMap,
28 fmt::Debug,
29 hash::{DefaultHasher, Hash, Hasher},
30 str::FromStr,
31 sync::{Arc, LazyLock, OnceLock},
32};
33
34static HOOK_OBJECTS: LazyLock<
36 ReentrantMutex<
37 RefCell<
38 HashMap<
39 i32,
40 Arc<
41 dyn Fn(&mut JNIEnv<'_>, &JObject<'_>, &JObjectArray<'_>) -> GlobalRef
42 + Send
43 + Sync,
44 >,
45 >,
46 >,
47 >,
48> = LazyLock::new(|| ReentrantMutex::new(RefCell::new(HashMap::new())));
49static HOOK_OBJECTS_OTHER: LazyLock<ReentrantMutex<RefCell<HashMap<u64, i32>>>> =
51 LazyLock::new(|| ReentrantMutex::new(RefCell::new(HashMap::new())));
52
53#[macro_export]
57macro_rules! import {
58 () => {
59 use std::{
60 rc::Rc,
61 sync::{Arc, Mutex},
62 };
63 use $crate::{
64 impl_array, null_value, to_java_byte_array, to_java_object_array, unbind_proxy_handler,
65 vm_attach, GlobalRef, JObject,
66 };
67
68 pub trait JObjRef {
73 fn java_ref(&self) -> GlobalRef;
77 }
78
79 pub trait JObjNew {
83 type Fields: Default;
85
86 fn _new(this: &GlobalRef, fields: Self::Fields) -> Self;
91
92 fn null() -> Self
96 where
97 Self: Sized,
98 Self::Fields: Default,
99 {
100 vm_attach!(mut env);
101 Self::_new(&null_value(&mut env), Default::default())
102 }
103 }
104
105 pub trait JType: JObjRef + JObjNew {
109 type Error;
111
112 const CLASS: &'static str;
114
115 const OBJECT_SIG: &'static str;
117
118 const DIM: u8 = 0;
120 }
121
122 pub trait JProxy: JObjNew + JObjRef {
126 fn new(fields: Self::Fields) -> std::sync::Arc<Self>;
131
132 fn release(&self) {
136 unbind_proxy_handler(&self.java_ref());
137 }
138 }
139
140 impl<T: JObjRef> JObjRef for &T {
141 fn java_ref(&self) -> GlobalRef {
142 self.java_ref()
143 }
144 }
145
146 impl<T: JObjNew> JObjNew for &T {
147 type Fields = T::Fields;
148
149 fn _new(this: &GlobalRef, fields: Self::Fields) -> Self {
150 panic!("Reference types cannot be constructed.")
151 }
152 }
153
154 impl<T: JObjRef + JObjNew> JObjRef for Option<T>
155 where
156 <T as JObjNew>::Fields: Default,
157 {
158 fn java_ref(&self) -> GlobalRef {
159 match self {
160 None => T::null().java_ref(),
161 Some(v) => v.java_ref(),
162 }
163 }
164 }
165
166 impl<T: JObjRef + JObjNew> JObjNew for Option<T> {
167 type Fields = T::Fields;
168
169 fn _new(this: &GlobalRef, fields: Self::Fields) -> Self {
170 match this.is_null() {
171 true => None,
172 false => Some(T::_new(this, fields)),
173 }
174 }
175 }
176
177 impl<T: JType> JType for Arc<T> {
178 const CLASS: &'static str = T::CLASS;
179 type Error = T::Error;
180 const OBJECT_SIG: &'static str = T::OBJECT_SIG;
181 }
182
183 impl<T: JObjRef> JObjRef for Arc<T> {
184 fn java_ref(&self) -> GlobalRef {
185 self.as_ref().java_ref()
186 }
187 }
188
189 impl<T: JObjNew> JObjNew for Arc<T> {
190 type Fields = T::Fields;
191
192 fn _new(this: &GlobalRef, fields: Self::Fields) -> Self {
193 T::_new(this, fields).into()
194 }
195 }
196
197 impl<T: JType> JType for Rc<T> {
198 const CLASS: &'static str = T::CLASS;
199 type Error = T::Error;
200 const OBJECT_SIG: &'static str = T::OBJECT_SIG;
201 }
202
203 impl<T: JObjRef> JObjRef for Rc<T> {
204 fn java_ref(&self) -> GlobalRef {
205 self.as_ref().java_ref()
206 }
207 }
208
209 impl<T: JObjNew> JObjNew for Rc<T> {
210 type Fields = T::Fields;
211
212 fn _new(this: &GlobalRef, fields: Self::Fields) -> Self {
213 T::_new(this, fields).into()
214 }
215 }
216
217 impl<T: JType> JType for Mutex<T> {
218 const CLASS: &'static str = T::CLASS;
219 type Error = T::Error;
220 const OBJECT_SIG: &'static str = T::OBJECT_SIG;
221 }
222
223 impl<T: JObjNew> JObjNew for Mutex<T> {
224 type Fields = T::Fields;
225
226 fn _new(this: &GlobalRef, fields: Self::Fields) -> Self {
227 T::_new(this, fields).into()
228 }
229 }
230
231 impl<T: JObjRef> JObjRef for Mutex<T> {
232 fn java_ref(&self) -> GlobalRef {
233 self.java_ref()
234 }
235 }
236
237 impl_array!(u8, 1);
238 impl_array!(String, 1);
239 };
240}
241
242#[macro_export]
251macro_rules! impl_array {
252 (String, $dim: expr) => {
253 impl JObjNew for &[String] {
254 type Fields = ();
255 fn _new(this: &GlobalRef, fields: Self::Fields) -> Self {
256 &[]
257 }
258 }
259
260 impl JObjRef for &[String] {
261 fn java_ref(&self) -> GlobalRef {
262 vm_attach!(mut env);
263 let arr = self
264 .iter()
265 .map(|i| env.new_string(i).unwrap())
266 .collect::<Vec<_>>();
267 let sig = if Self::DIM <= 1 {
268 Self::CLASS.to_string()
269 } else {
270 "[".repeat((Self::DIM - 1) as _) + Self::OBJECT_SIG
271 };
272 let arr = to_java_object_array(&mut env, &arr, &sig);
273 env.new_global_ref(&arr).unwrap()
274 }
275 }
276
277 impl JType for &[String] {
278 type Error = <String as JType>::Error;
279 const CLASS: &'static str = <String as JType>::CLASS;
280 const OBJECT_SIG: &'static str = <String as JType>::OBJECT_SIG;
281 const DIM: u8 = $dim;
282 }
283 };
284 (u8, $dim:expr) => {
285 impl JObjNew for &[u8] {
286 type Fields = ();
287 fn _new(this: &GlobalRef, fields: Self::Fields) -> Self {
288 &[]
289 }
290 }
291
292 impl JObjRef for &[u8] {
293 fn java_ref(&self) -> GlobalRef {
294 vm_attach!(mut env);
295 let arr = self.iter().map(|i| *i as _).collect::<Vec<_>>();
296 let arr = to_java_byte_array(&mut env, &arr);
297 env.new_global_ref(&arr).unwrap()
298 }
299 }
300
301 impl JType for &[u8] {
302 type Error = <String as JType>::Error;
303 const CLASS: &'static str = "B";
304 const OBJECT_SIG: &'static str = "B";
305 const DIM: u8 = $dim;
306 }
307 };
308}
309
310pub fn android_vm<'a>() -> JavaVM {
314 let ctx = ndk_context::android_context();
315 unsafe { JavaVM::from_raw(ctx.vm().cast()) }.unwrap()
316}
317
318#[macro_export]
330macro_rules! vm_attach {
331 (mut $var:ident) => {
332 let vm = $crate::android_vm();
333 let mut $var = vm.attach_current_thread().unwrap();
334 };
335 (_ $var:ident) => {
336 let vm = android_vm();
337 let $var = vm.attach_current_thread().unwrap()
338 };
339}
340
341pub fn android_context<'a>() -> JObject<'a> {
345 let ctx = ndk_context::android_context();
346 unsafe { JObject::from_raw(ctx.context().cast()) }
347}
348
349pub fn new_proxy(interfaces: &[&str]) -> GlobalRef {
366 let class = load_rust_call_method_hook_class();
367 vm_attach!(mut env);
368 let obj = env.new_object(class, "()V", &[]).unwrap();
369 let faces = env
370 .new_object_array(
371 interfaces.len() as jsize,
372 "java/lang/Class",
373 &JObject::null(),
374 )
375 .unwrap();
376 for i in 0..interfaces.len() {
377 let class = env.new_string(interfaces[i]).unwrap();
378 let face = env
379 .call_static_method(
380 "java/lang/Class",
381 "forName",
382 "(Ljava/lang/String;)Ljava/lang/Class;",
383 &[(&class).into()],
384 )
385 .unwrap()
386 .l()
387 .unwrap();
388 env.set_object_array_element(&faces, i as jsize, &face)
389 .unwrap();
390 }
391 let hash_code = env
392 .call_method(&obj, "hashCode", "()I", &[])
393 .unwrap()
394 .i()
395 .unwrap();
396 let res = env.call_static_method(
397 "java/lang/reflect/Proxy",
398 "newProxyInstance",
399 "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;",
400 &[
401 (&JObject::null()).into(),
402 (&faces).into(),
403 (&obj).into()
404 ]
405 ).unwrap()
406 .l()
407 .unwrap();
408 let res = env.new_global_ref(&res).unwrap();
409 let lock = HOOK_OBJECTS_OTHER.lock();
410 let mut hasher = DefaultHasher::new();
411 res.hash(&mut hasher);
412 lock.borrow_mut().insert(hasher.finish(), hash_code);
413 drop(lock);
414 res
415}
416
417pub fn bind_proxy_handler(
442 proxy: &GlobalRef,
443 handler: impl Fn(&mut JNIEnv<'_>, &JObject<'_>, &JObjectArray<'_>) -> GlobalRef
444 + Send
445 + Sync
446 + 'static,
447) {
448 let hash_code = get_proxy_hash_code(proxy);
449 let lock = match HOOK_OBJECTS.try_lock() {
450 Some(lock) => lock,
451 None => {
452 error!("Can't bind proxy handler.");
453 return;
454 }
455 };
456 lock.borrow_mut().insert(hash_code, Arc::new(handler));
457}
458
459pub fn get_proxy_hash_code(proxy: &GlobalRef) -> i32 {
475 let mut hasher = DefaultHasher::new();
476 proxy.hash(&mut hasher);
477 let proxy_hash_code = hasher.finish();
478 let lock = HOOK_OBJECTS_OTHER.lock();
479 if let Some(code) = lock.borrow().get(&proxy_hash_code) {
480 return *code;
481 };
482 0
483}
484
485pub fn unbind_proxy_handler(proxy: &GlobalRef) {
502 let mut hasher = DefaultHasher::new();
503 proxy.hash(&mut hasher);
504 let proxy_hash_code = hasher.finish();
505 let lock = HOOK_OBJECTS_OTHER.lock();
506 if let Some(code) = lock.borrow().get(&proxy_hash_code) {
507 let lock = HOOK_OBJECTS.lock();
508 lock.borrow_mut().remove_entry(code);
509 debug!("Proxy `{}` is dropped.", proxy_hash_code);
510 };
511}
512
513pub trait ParseJObjectType<T: FromStr> {
517 fn parse(&self, env: &mut JNIEnv) -> T
518 where
519 <T as FromStr>::Err: Debug;
520}
521
522impl<T: FromStr> ParseJObjectType<T> for JObject<'_> {
523 fn parse(&self, env: &mut JNIEnv) -> T
525 where
526 <T as FromStr>::Err: Debug,
527 {
528 let s = env
529 .call_method(self, "toString", "()Ljava/lang/String;", &[])
530 .unwrap()
531 .l()
532 .unwrap();
533 let s = env.get_string((&s).into()).unwrap();
534 s.to_str().unwrap().parse().unwrap()
535 }
536}
537
538fn load_rust_call_method_hook_class<'a>() -> &'a GlobalRef {
540 #[cfg(target_os = "android")]
541 const BYTECODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/classes.dex"));
542 #[cfg(not(target_os = "android"))]
543 const BYTECODE: &[u8] = &[];
544 const LOADER_CLASS: &str = "dalvik/system/InMemoryDexClassLoader";
545 static INSTANCE: OnceLock<GlobalRef> = OnceLock::new();
546
547 INSTANCE.get_or_init(|| {
548 vm_attach!(mut env);
549 let byte_buffer = unsafe { env.new_direct_byte_buffer(BYTECODE.as_ptr() as *mut u8, BYTECODE.len()) }.unwrap();
550
551 let dex_class_loader = env
552 .new_object(
553 LOADER_CLASS,
554 "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V",
555 &[
556 JValueGen::Object(&JObject::from(byte_buffer)),
557 JValueGen::Object(&JObject::null()),
558 ],
559 ).unwrap();
560
561 let class = env.new_string("rust/CallMethodHook").unwrap();
562 let class = env
563 .call_method(
564 &dex_class_loader,
565 "loadClass",
566 "(Ljava/lang/String;)Ljava/lang/Class;",
567 &[(&class).into()],
568 )
569 .unwrap()
570 .l()
571 .unwrap();
572 let m = NativeMethod {
573 name: "invoke".into(),
574 sig: "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;".into(),
575 fn_ptr: rust_callback as *mut _,
576 };
577 env.register_native_methods(Into::<&JClass<'_>>::into(&class), &[m]).unwrap();
578
579 env.new_global_ref(&class).unwrap()
580 })
581}
582
583unsafe extern "C" fn rust_callback<'a>(
585 mut env: JNIEnv<'a>,
586 this: JObject<'a>,
587 _: JObject<'a>,
588 method: JObject<'a>,
589 args: JObjectArray<'a>,
590) -> JObject<'a> {
591 let hash_code = env
592 .call_method(&this, "hashCode", "()I", &[])
593 .unwrap()
594 .i()
595 .unwrap_or(0);
596
597 let name = match env.call_method(&method, "getName", "()Ljava/lang/String;", &[]) {
598 Ok(name) => name.l(),
599 Err(e) => {
600 error!("{}", e);
601 return JObject::null();
602 }
603 };
604 let name = match name {
605 Ok(name) => name,
606 Err(e) => {
607 error!("{}", e);
608 return JObject::null();
609 }
610 };
611 let name = match env.get_string((&name).into()) {
612 Ok(name) => name,
613 Err(e) => {
614 error!("{}", e);
615 return JObject::null();
616 }
617 };
618 match name.to_str() {
619 Ok(name) => match name {
620 "toString" => {
621 return env
622 .new_string(format!("Proxy@{:x}", hash_code).as_str())
623 .unwrap()
624 .into()
625 }
626 "equals" | "hashCode" => {
627 return env
628 .call_method(
629 &method,
630 "invoke",
631 "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
632 &[(&this).into(), (&args).into()],
633 )
634 .unwrap()
635 .l()
636 .unwrap()
637 }
638 _ => (),
639 },
640 Err(e) => {
641 error!("{}", e);
642 return JObject::null();
643 }
644 }
645
646 let lock = HOOK_OBJECTS.lock();
647 let func = if let Some(f) = lock.borrow().get(&hash_code) {
648 f.to_owned()
649 } else {
650 warn!(
651 "The method call has reached, but it appears that the proxy object has been dropped."
652 );
653 return JObject::null();
654 };
655 drop(lock);
656 let ret = func(&mut env, &method, &args);
657 env.new_local_ref(ret.as_obj()).unwrap()
658}
659
660pub fn to_vec<'a>(env: &mut JNIEnv<'a>, arr: &JObjectArray) -> Vec<JObject<'a>> {
681 let Ok(size) = env.get_array_length(arr) else {
682 return vec![];
683 };
684 let mut arr2 = Vec::with_capacity(size as usize);
685 for i in 0..size {
686 arr2.push(env.get_object_array_element(arr, i).unwrap());
687 }
688 arr2
689}
690
691pub fn to_java_object_array<'a, O: AsRef<JObject<'a>>>(
719 env: &mut JNIEnv<'a>,
721 arr: &[O],
723 element_class: &str,
725) -> JObjectArray<'a> {
726 let arr2 = env
728 .new_object_array(arr.len() as _, element_class, JObject::null())
729 .unwrap();
730 for (i, j) in arr.iter().enumerate() {
732 env.set_object_array_element(&arr2, i as _, j).unwrap();
733 }
734 arr2
736}
737
738pub fn to_java_byte_array<'a>(
766 env: &mut JNIEnv<'a>,
768 arr: &[jbyte],
770) -> JByteArray<'a> {
771 let arr2 = env.new_byte_array(arr.len() as _).unwrap();
773 env.set_byte_array_region(&arr2, 0, arr).unwrap();
775 arr2
777}
778
779pub fn null_value(env: &mut JNIEnv) -> GlobalRef {
795 let obj = JObject::null();
796 env.new_global_ref(&obj).unwrap()
797}
798
799pub fn wrapper_bool_value(value: bool, env: &mut JNIEnv) -> GlobalRef {
816 let obj = env
817 .new_object("java/lang/Boolean", "(Z)V", &[(value as jboolean).into()])
818 .unwrap();
819 env.new_global_ref(&obj).unwrap()
820}
821
822pub fn wrapper_integer_value(value: i32, env: &mut JNIEnv) -> GlobalRef {
839 let obj = env
840 .new_object("java/lang/Integer", "(I)V", &[value.into()])
841 .unwrap();
842 env.new_global_ref(&obj).unwrap()
843}
844
845pub fn wrapper_long_value(value: i64, env: &mut JNIEnv) -> GlobalRef {
862 let obj = env
863 .new_object("java/lang/Long", "(J)V", &[value.into()])
864 .unwrap();
865 env.new_global_ref(&obj).unwrap()
866}