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