• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Implementation of JNI platform functionality.
16 use crate::jnames::{SEND_REQUEST_MNAME, SEND_REQUEST_MSIG};
17 use crate::unique_jvm;
18 use anyhow::anyhow;
19 use jni::errors::Error as JNIError;
20 use jni::objects::{GlobalRef, JMethodID, JObject, JValue};
21 use jni::signature::TypeSignature;
22 use jni::sys::{jbyteArray, jint, jlong, jvalue};
23 use jni::{JNIEnv, JavaVM};
24 use log::{debug, error, info};
25 use std::collections::HashMap;
26 use std::sync::{
27     atomic::{AtomicI64, Ordering},
28     Arc, LazyLock, Mutex,
29 };
30 
31 /// Macro capturing the name of the function calling this macro.
32 ///
33 /// function_name()! -> &'static str
34 /// Returns the function name as 'static reference.
35 macro_rules! function_name {
36     () => {{
37         // Declares function f inside current function.
38         fn f() {}
39         fn type_name_of<T>(_: T) -> &'static str {
40             std::any::type_name::<T>()
41         }
42         // type name of f is struct_or_crate_name::calling_function_name::f
43         let name = type_name_of(f);
44         // Find and cut the rest of the path:
45         // Third to last character, up to the first semicolon: is calling_function_name
46         match &name[..name.len() - 3].rfind(':') {
47             Some(pos) => &name[pos + 1..name.len() - 3],
48             None => &name[..name.len() - 3],
49         }
50     }};
51 }
52 
53 static HANDLE_MAPPING: LazyLock<Mutex<HashMap<i64, Arc<Mutex<JavaPlatform>>>>> =
54     LazyLock::new(|| Mutex::new(HashMap::new()));
55 static HANDLE_RN: AtomicI64 = AtomicI64::new(0);
56 
generate_platform_handle() -> i6457 fn generate_platform_handle() -> i64 {
58     HANDLE_RN.fetch_add(1, Ordering::SeqCst)
59 }
60 
insert_platform_handle(handle: i64, item: Arc<Mutex<JavaPlatform>>)61 fn insert_platform_handle(handle: i64, item: Arc<Mutex<JavaPlatform>>) {
62     if 0 == handle {
63         // Init once
64         logger::init(
65             logger::Config::default()
66                 .with_tag_on_device("remoteauth")
67                 .with_max_level(log::LevelFilter::Trace)
68                 .with_filter("trace,jni=info"),
69         );
70     }
71     HANDLE_MAPPING.lock().unwrap().insert(handle, Arc::clone(&item));
72 }
73 
74 /// Reports a response from remote device.
75 pub trait ResponseCallback {
76     /// Invoked upon successful response
on_response(&mut self, response: Vec<u8>)77     fn on_response(&mut self, response: Vec<u8>);
78     /// Invoked upon failure
on_error(&mut self, error_code: i32)79     fn on_error(&mut self, error_code: i32);
80 }
81 
82 /// Trait to platform functionality
83 pub trait Platform {
84     /// Send a binary message to the remote with the given connection id and return the response.
send_request( &mut self, connection_id: i32, request: &[u8], callback: Box<dyn ResponseCallback + Send>, ) -> anyhow::Result<()>85     fn send_request(
86         &mut self,
87         connection_id: i32,
88         request: &[u8],
89         callback: Box<dyn ResponseCallback + Send>,
90     ) -> anyhow::Result<()>;
91 }
92 //////////////////////////////////
93 
94 /// Implementation of Platform trait
95 pub struct JavaPlatform {
96     platform_handle: i64,
97     vm: &'static Arc<JavaVM>,
98     platform_native_obj: GlobalRef,
99     send_request_method_id: JMethodID,
100     map_futures: Mutex<HashMap<i64, Box<dyn ResponseCallback + Send>>>,
101     atomic_handle: AtomicI64,
102 }
103 
104 impl JavaPlatform {
105     /// Creates JavaPlatform and associates with unique handle id
create( java_platform_native: JObject<'_>, ) -> Result<Arc<Mutex<impl Platform>>, JNIError>106     pub fn create(
107         java_platform_native: JObject<'_>,
108     ) -> Result<Arc<Mutex<impl Platform>>, JNIError> {
109         let platform_handle = generate_platform_handle();
110         let platform = Arc::new(Mutex::new(JavaPlatform::new(
111             platform_handle,
112             unique_jvm::get_static_ref().ok_or(JNIError::InvalidCtorReturn)?,
113             java_platform_native,
114         )?));
115         insert_platform_handle(platform_handle, Arc::clone(&platform));
116         Ok(Arc::clone(&platform))
117     }
118 
new( platform_handle: i64, vm: &'static Arc<JavaVM>, java_platform_native: JObject, ) -> Result<JavaPlatform, JNIError>119     fn new(
120         platform_handle: i64,
121         vm: &'static Arc<JavaVM>,
122         java_platform_native: JObject,
123     ) -> Result<JavaPlatform, JNIError> {
124         vm.attach_current_thread().and_then(|env| {
125             let platform_class = env.get_object_class(java_platform_native)?;
126             let platform_native_obj = env.new_global_ref(java_platform_native)?;
127             let send_request_method: JMethodID =
128                 env.get_method_id(platform_class, SEND_REQUEST_MNAME, SEND_REQUEST_MSIG)?;
129 
130             Ok(Self {
131                 platform_handle,
132                 vm,
133                 platform_native_obj,
134                 send_request_method_id: send_request_method,
135                 map_futures: Mutex::new(HashMap::new()),
136                 atomic_handle: AtomicI64::new(0),
137             })
138         })
139     }
140 }
141 
142 impl Platform for JavaPlatform {
143     #[allow(clippy::unit_arg)]
send_request( &mut self, connection_id: i32, request: &[u8], callback: Box<dyn ResponseCallback + Send>, ) -> anyhow::Result<()>144     fn send_request(
145         &mut self,
146         connection_id: i32,
147         request: &[u8],
148         callback: Box<dyn ResponseCallback + Send>,
149     ) -> anyhow::Result<()> {
150         let type_signature = TypeSignature::from_str(SEND_REQUEST_MSIG)
151             .map_err(|e| anyhow!("JNI: Invalid type signature: {:?}", e))?;
152 
153         let response_handle = self.atomic_handle.fetch_add(1, Ordering::SeqCst);
154         self.map_futures.lock().unwrap().insert(response_handle, callback);
155         self.vm
156             .attach_current_thread()
157             .and_then(|env| {
158                 let request_jbytearray = env.byte_array_from_slice(request)?;
159                 // Safety: request_jbytearray is safely instantiated above.
160                 let request_jobject = unsafe { JObject::from_raw(request_jbytearray) };
161 
162                 let _ = env.call_method_unchecked(
163                     self.platform_native_obj.as_obj(),
164                     self.send_request_method_id,
165                     type_signature.ret,
166                     &[
167                         jvalue::from(JValue::Int(connection_id)),
168                         jvalue::from(JValue::Object(request_jobject)),
169                         jvalue::from(JValue::Long(response_handle)),
170                         jvalue::from(JValue::Long(self.platform_handle)),
171                     ],
172                 );
173                 Ok(info!(
174                     "{} successfully sent-message, waiting for response {}:{}",
175                     function_name!(),
176                     self.platform_handle,
177                     response_handle
178                 ))
179             })
180             .map_err(|e| anyhow!("JNI: Failed to attach current thread: {:?}", e))?;
181         Ok(())
182     }
183 }
184 
185 impl JavaPlatform {
on_send_request_success(&mut self, response: &[u8], response_handle: i64)186     fn on_send_request_success(&mut self, response: &[u8], response_handle: i64) {
187         info!(
188             "{} completed successfully {}:{}",
189             function_name!(),
190             self.platform_handle,
191             response_handle
192         );
193         if let Some(mut callback) = self.map_futures.lock().unwrap().remove(&response_handle) {
194             callback.on_response(response.to_vec());
195         } else {
196             error!(
197                 "Failed to find TX for {} and {}:{}",
198                 function_name!(),
199                 self.platform_handle,
200                 response_handle
201             );
202         }
203     }
204 
on_send_request_error(&self, error_code: i32, response_handle: i64)205     fn on_send_request_error(&self, error_code: i32, response_handle: i64) {
206         error!(
207             "{} completed with error {} {}:{}",
208             function_name!(),
209             error_code,
210             self.platform_handle,
211             response_handle
212         );
213         if let Some(mut callback) = self.map_futures.lock().unwrap().remove(&response_handle) {
214             callback.on_error(error_code);
215         } else {
216             error!(
217                 "Failed to find callback for {} and {}:{}",
218                 function_name!(),
219                 self.platform_handle,
220                 response_handle
221             );
222         }
223     }
224 }
225 
226 /// Returns successful response from remote device
227 #[no_mangle]
Java_com_android_server_remoteauth_jni_NativeRemoteAuthJavaPlatform_native_on_send_request_success( env: JNIEnv, _: JObject, app_response: jbyteArray, platform_handle: jlong, response_handle: jlong, )228 pub extern "system" fn Java_com_android_server_remoteauth_jni_NativeRemoteAuthJavaPlatform_native_on_send_request_success(
229     env: JNIEnv,
230     _: JObject,
231     app_response: jbyteArray,
232     platform_handle: jlong,
233     response_handle: jlong,
234 ) {
235     debug!("{}: enter", function_name!());
236     native_on_send_request_success(env, app_response, platform_handle, response_handle);
237 }
238 
native_on_send_request_success( env: JNIEnv<'_>, app_response: jbyteArray, platform_handle: jlong, response_handle: jlong, )239 fn native_on_send_request_success(
240     env: JNIEnv<'_>,
241     app_response: jbyteArray,
242     platform_handle: jlong,
243     response_handle: jlong,
244 ) {
245     if let Some(platform) = HANDLE_MAPPING.lock().unwrap().get(&platform_handle) {
246         let response =
247             env.convert_byte_array(app_response).map_err(|_| JNIError::InvalidCtorReturn).unwrap();
248         let mut platform = (*platform).lock().unwrap();
249         platform.on_send_request_success(&response, response_handle);
250     } else {
251         let _ = env.throw_new(
252             "com/android/server/remoteauth/jni/BadHandleException",
253             format!("Failed to find Platform with ID {} in {}", platform_handle, function_name!()),
254         );
255     }
256 }
257 
258 /// Notifies about failure to receive a response from remote device
259 #[no_mangle]
Java_com_android_server_remoteauth_jni_NativeRemoteAuthJavaPlatform_native_on_send_request_error( env: JNIEnv, _: JObject, error_code: jint, platform_handle: jlong, response_handle: jlong, )260 pub extern "system" fn Java_com_android_server_remoteauth_jni_NativeRemoteAuthJavaPlatform_native_on_send_request_error(
261     env: JNIEnv,
262     _: JObject,
263     error_code: jint,
264     platform_handle: jlong,
265     response_handle: jlong,
266 ) {
267     debug!("{}: enter", function_name!());
268     native_on_send_request_error(env, error_code, platform_handle, response_handle);
269 }
270 
native_on_send_request_error( env: JNIEnv<'_>, error_code: jint, platform_handle: jlong, response_handle: jlong, )271 fn native_on_send_request_error(
272     env: JNIEnv<'_>,
273     error_code: jint,
274     platform_handle: jlong,
275     response_handle: jlong,
276 ) {
277     if let Some(platform) = HANDLE_MAPPING.lock().unwrap().get(&platform_handle) {
278         let platform = (*platform).lock().unwrap();
279         platform.on_send_request_error(error_code, response_handle);
280     } else {
281         let _ = env.throw_new(
282             "com/android/server/remoteauth/jni/BadHandleException",
283             format!("Failed to find Platform with ID {} in {}", platform_handle, function_name!()),
284         );
285     }
286 }
287 
288 #[cfg(test)]
289 mod tests {
290     //use super::*;
291 
292     //use tokio::runtime::Builder;
293 
294     /// Checks validity of the function_name! macro.
295     #[test]
test_function_name()296     fn test_function_name() {
297         assert_eq!(function_name!(), "test_function_name");
298     }
299 }
300