1use core::ffi::c_void;
4use std::panic::{catch_unwind, AssertUnwindSafe};
5use std::ptr::NonNull;
6
7use crate::ffi;
8use crate::la_context::{BiometryType, LACompanionType};
9use crate::la_error::{LAError, Result};
10use crate::private::{
11 bridge_bool, bridge_bytes, bridge_i32, bridge_i64, bridge_opt_bytes, bridge_opt_ptr,
12 bridge_ptr, bridge_string, bridge_unit, OwnedHandle,
13};
14
15fn mechanism_is_usable(ptr: *mut c_void) -> Result<bool> {
16 bridge_bool(|out, error_out| unsafe {
17 ffi::la_environment::la_environment_mechanism_get_is_usable(ptr, out, error_out)
18 })
19}
20
21fn mechanism_localized_name(ptr: *mut c_void) -> Result<String> {
22 bridge_string(|out, error_out| unsafe {
23 ffi::la_environment::la_environment_mechanism_get_localized_name(ptr, out, error_out)
24 })
25}
26
27fn mechanism_icon_system_name(ptr: *mut c_void) -> Result<String> {
28 bridge_string(|out, error_out| unsafe {
29 ffi::la_environment::la_environment_mechanism_get_icon_system_name(ptr, out, error_out)
30 })
31}
32
33fn count_to_usize(count: i64, label: &str) -> Result<usize> {
34 usize::try_from(count)
35 .map_err(|_| LAError::BridgeFailed(format!("LocalAuthentication returned an invalid {label} count")))
36}
37
38pub trait LAEnvironmentObserver: Send + Sync + 'static {
40 fn state_did_change(&self, environment: &LAEnvironment, old_state: &LAEnvironmentState);
42}
43
44impl<F> LAEnvironmentObserver for F
45where
46 F: Fn(&LAEnvironment, &LAEnvironmentState) + Send + Sync + 'static,
47{
48 fn state_did_change(&self, environment: &LAEnvironment, old_state: &LAEnvironmentState) {
49 self(environment, old_state);
50 }
51}
52
53struct EnvironmentObserverContext {
54 observer: Box<dyn LAEnvironmentObserver>,
55}
56
57unsafe extern "C" fn environment_observer_trampoline(
58 context: *mut c_void,
59 environment_ptr: *mut c_void,
60 old_state_ptr: *mut c_void,
61) {
62 let Some(context) = NonNull::new(context.cast::<EnvironmentObserverContext>()) else {
63 return;
64 };
65 let Some(environment_raw) = NonNull::new(environment_ptr) else {
66 return;
67 };
68 let Some(old_state_raw) = NonNull::new(old_state_ptr) else {
69 return;
70 };
71
72 let context = unsafe { context.as_ref() };
73 let environment = LAEnvironment::from_raw(environment_raw);
74 let old_state = LAEnvironmentState::from_raw(old_state_raw);
75
76 let _ = catch_unwind(AssertUnwindSafe(|| {
77 context.observer.state_did_change(&environment, &old_state);
78 }));
79}
80
81unsafe extern "C" fn environment_observer_release(context: *mut c_void) {
82 if let Some(context) = NonNull::new(context.cast::<EnvironmentObserverContext>()) {
83 unsafe { drop(Box::from_raw(context.as_ptr())) };
84 }
85}
86
87#[derive(Debug)]
89pub struct LAEnvironmentObserverRegistration {
90 handle: OwnedHandle,
91}
92
93impl LAEnvironmentObserverRegistration {
94 pub(crate) const fn as_ptr(&self) -> *mut c_void {
95 self.handle.as_ptr()
96 }
97}
98
99#[derive(Debug)]
101pub struct LAEnvironment {
102 handle: OwnedHandle,
103}
104
105impl LAEnvironment {
106 pub(crate) fn from_raw(raw: NonNull<c_void>) -> Self {
107 Self {
108 handle: OwnedHandle::new(raw, ffi::la_environment::la_environment_release),
109 }
110 }
111
112 #[allow(dead_code)]
113 pub(crate) const fn as_ptr(&self) -> *mut c_void {
114 self.handle.as_ptr()
115 }
116
117 pub fn current_user() -> Result<Self> {
123 Ok(Self::from_raw(bridge_ptr(|out, error_out| unsafe {
124 ffi::la_environment::la_environment_current_user(out, error_out)
125 })?))
126 }
127
128 pub fn state(&self) -> Result<LAEnvironmentState> {
134 Ok(LAEnvironmentState::from_raw(bridge_ptr(
135 |out, error_out| unsafe {
136 ffi::la_environment::la_environment_get_state(self.handle.as_ptr(), out, error_out)
137 },
138 )?))
139 }
140
141 pub fn add_observer<O>(&self, observer: O) -> Result<LAEnvironmentObserverRegistration>
150 where
151 O: LAEnvironmentObserver,
152 {
153 let context = Box::new(EnvironmentObserverContext {
154 observer: Box::new(observer),
155 });
156 let context_ptr = Box::into_raw(context).cast::<c_void>();
157
158 let observer_raw = match bridge_ptr(|out, error_out| unsafe {
159 ffi::la_environment::la_environment_observer_new(
160 Some(environment_observer_trampoline),
161 Some(environment_observer_release),
162 context_ptr,
163 out,
164 error_out,
165 )
166 }) {
167 Ok(raw) => raw,
168 Err(error) => {
169 unsafe { environment_observer_release(context_ptr) };
170 return Err(error);
171 }
172 };
173
174 let registration = LAEnvironmentObserverRegistration {
175 handle: OwnedHandle::new(
176 observer_raw,
177 ffi::la_environment::la_environment_observer_release,
178 ),
179 };
180
181 if let Err(error) = bridge_unit(|error_out| unsafe {
182 ffi::la_environment::la_environment_add_observer(
183 self.handle.as_ptr(),
184 registration.handle.as_ptr(),
185 error_out,
186 )
187 }) {
188 drop(registration);
189 return Err(error);
190 }
191
192 Ok(registration)
193 }
194
195 pub fn remove_observer(&self, observer: &LAEnvironmentObserverRegistration) -> Result<()> {
201 bridge_unit(|error_out| unsafe {
202 ffi::la_environment::la_environment_remove_observer(
203 self.handle.as_ptr(),
204 observer.as_ptr(),
205 error_out,
206 )
207 })
208 }
209}
210
211#[derive(Debug)]
213pub struct LAEnvironmentState {
214 handle: OwnedHandle,
215}
216
217impl LAEnvironmentState {
218 pub(crate) fn from_raw(raw: NonNull<c_void>) -> Self {
219 Self {
220 handle: OwnedHandle::new(raw, ffi::la_environment::la_environment_state_release),
221 }
222 }
223
224 #[allow(dead_code)]
225 pub(crate) const fn as_ptr(&self) -> *mut c_void {
226 self.handle.as_ptr()
227 }
228
229 pub fn biometry(&self) -> Result<Option<LAEnvironmentMechanismBiometry>> {
235 let raw = bridge_opt_ptr(|out, error_out| unsafe {
236 ffi::la_environment::la_environment_state_get_biometry(self.handle.as_ptr(), out, error_out)
237 })?;
238 Ok(raw.map(LAEnvironmentMechanismBiometry::from_raw))
239 }
240
241 pub fn user_password(&self) -> Result<Option<LAEnvironmentMechanismUserPassword>> {
247 let raw = bridge_opt_ptr(|out, error_out| unsafe {
248 ffi::la_environment::la_environment_state_get_user_password(
249 self.handle.as_ptr(),
250 out,
251 error_out,
252 )
253 })?;
254 Ok(raw.map(LAEnvironmentMechanismUserPassword::from_raw))
255 }
256
257 pub fn companions(&self) -> Result<Vec<LAEnvironmentMechanismCompanion>> {
263 let count = count_to_usize(
264 bridge_i64(|out, error_out| unsafe {
265 ffi::la_environment::la_environment_state_get_companion_count(
266 self.handle.as_ptr(),
267 out,
268 error_out,
269 )
270 })?,
271 "companion",
272 )?;
273
274 let mut mechanisms = Vec::with_capacity(count);
275 for index in 0..count {
276 let index = i64::try_from(index).map_err(|_| {
277 LAError::BridgeFailed(
278 "LocalAuthentication returned more companion mechanisms than this platform can index"
279 .to_owned(),
280 )
281 })?;
282 let raw = bridge_ptr(|out, error_out| unsafe {
283 ffi::la_environment::la_environment_state_get_companion_at(
284 self.handle.as_ptr(),
285 index,
286 out,
287 error_out,
288 )
289 })?;
290 mechanisms.push(LAEnvironmentMechanismCompanion::from_raw(raw));
291 }
292 Ok(mechanisms)
293 }
294
295 pub fn all_mechanisms(&self) -> Result<Vec<LAEnvironmentMechanism>> {
301 let count = count_to_usize(
302 bridge_i64(|out, error_out| unsafe {
303 ffi::la_environment::la_environment_state_get_all_mechanism_count(
304 self.handle.as_ptr(),
305 out,
306 error_out,
307 )
308 })?,
309 "mechanism",
310 )?;
311
312 let mut mechanisms = Vec::with_capacity(count);
313 for index in 0..count {
314 let index = i64::try_from(index).map_err(|_| {
315 LAError::BridgeFailed(
316 "LocalAuthentication returned more mechanisms than this platform can index"
317 .to_owned(),
318 )
319 })?;
320 let raw = bridge_ptr(|out, error_out| unsafe {
321 ffi::la_environment::la_environment_state_get_all_mechanism_at(
322 self.handle.as_ptr(),
323 index,
324 out,
325 error_out,
326 )
327 })?;
328 mechanisms.push(LAEnvironmentMechanism::from_raw(raw));
329 }
330 Ok(mechanisms)
331 }
332}
333
334#[derive(Debug)]
336pub struct LAEnvironmentMechanism {
337 handle: OwnedHandle,
338}
339
340impl LAEnvironmentMechanism {
341 pub(crate) fn from_raw(raw: NonNull<c_void>) -> Self {
342 Self {
343 handle: OwnedHandle::new(raw, ffi::la_environment::la_environment_mechanism_release),
344 }
345 }
346
347 #[allow(dead_code)]
348 pub(crate) const fn as_ptr(&self) -> *mut c_void {
349 self.handle.as_ptr()
350 }
351
352 pub fn is_usable(&self) -> Result<bool> {
358 mechanism_is_usable(self.handle.as_ptr())
359 }
360
361 pub fn localized_name(&self) -> Result<String> {
367 mechanism_localized_name(self.handle.as_ptr())
368 }
369
370 pub fn icon_system_name(&self) -> Result<String> {
376 mechanism_icon_system_name(self.handle.as_ptr())
377 }
378}
379
380#[derive(Debug)]
382pub struct LAEnvironmentMechanismBiometry {
383 handle: OwnedHandle,
384}
385
386impl LAEnvironmentMechanismBiometry {
387 pub(crate) fn from_raw(raw: NonNull<c_void>) -> Self {
388 Self {
389 handle: OwnedHandle::new(raw, ffi::la_environment::la_environment_mechanism_release),
390 }
391 }
392
393 #[allow(dead_code)]
394 pub(crate) const fn as_ptr(&self) -> *mut c_void {
395 self.handle.as_ptr()
396 }
397
398 pub fn is_usable(&self) -> Result<bool> {
404 mechanism_is_usable(self.handle.as_ptr())
405 }
406
407 pub fn localized_name(&self) -> Result<String> {
413 mechanism_localized_name(self.handle.as_ptr())
414 }
415
416 pub fn icon_system_name(&self) -> Result<String> {
422 mechanism_icon_system_name(self.handle.as_ptr())
423 }
424
425 pub fn biometry_type(&self) -> Result<BiometryType> {
431 Ok(BiometryType::from_ffi(bridge_i32(|out, error_out| unsafe {
432 ffi::la_environment::la_environment_mechanism_biometry_get_biometry_type(
433 self.handle.as_ptr(),
434 out,
435 error_out,
436 )
437 })?))
438 }
439
440 pub fn is_enrolled(&self) -> Result<bool> {
446 bridge_bool(|out, error_out| unsafe {
447 ffi::la_environment::la_environment_mechanism_biometry_get_is_enrolled(
448 self.handle.as_ptr(),
449 out,
450 error_out,
451 )
452 })
453 }
454
455 pub fn is_locked_out(&self) -> Result<bool> {
461 bridge_bool(|out, error_out| unsafe {
462 ffi::la_environment::la_environment_mechanism_biometry_get_is_locked_out(
463 self.handle.as_ptr(),
464 out,
465 error_out,
466 )
467 })
468 }
469
470 pub fn state_hash(&self) -> Result<Vec<u8>> {
476 bridge_bytes(|out, out_len, error_out| unsafe {
477 ffi::la_environment::la_environment_mechanism_biometry_get_state_hash(
478 self.handle.as_ptr(),
479 out,
480 out_len,
481 error_out,
482 )
483 })
484 }
485
486 pub fn built_in_sensor_inaccessible(&self) -> Result<bool> {
492 bridge_bool(|out, error_out| unsafe {
493 ffi::la_environment::la_environment_mechanism_biometry_get_built_in_sensor_inaccessible(
494 self.handle.as_ptr(),
495 out,
496 error_out,
497 )
498 })
499 }
500}
501
502#[derive(Debug)]
504pub struct LAEnvironmentMechanismCompanion {
505 handle: OwnedHandle,
506}
507
508impl LAEnvironmentMechanismCompanion {
509 pub(crate) fn from_raw(raw: NonNull<c_void>) -> Self {
510 Self {
511 handle: OwnedHandle::new(raw, ffi::la_environment::la_environment_mechanism_release),
512 }
513 }
514
515 #[allow(dead_code)]
516 pub(crate) const fn as_ptr(&self) -> *mut c_void {
517 self.handle.as_ptr()
518 }
519
520 pub fn is_usable(&self) -> Result<bool> {
526 mechanism_is_usable(self.handle.as_ptr())
527 }
528
529 pub fn localized_name(&self) -> Result<String> {
535 mechanism_localized_name(self.handle.as_ptr())
536 }
537
538 pub fn icon_system_name(&self) -> Result<String> {
544 mechanism_icon_system_name(self.handle.as_ptr())
545 }
546
547 pub fn companion_type(&self) -> Result<LACompanionType> {
553 Ok(LACompanionType::from_ffi(bridge_i32(|out, error_out| unsafe {
554 ffi::la_environment::la_environment_mechanism_companion_get_type(
555 self.handle.as_ptr(),
556 out,
557 error_out,
558 )
559 })?))
560 }
561
562 pub fn state_hash(&self) -> Result<Option<Vec<u8>>> {
568 bridge_opt_bytes(|out, out_len, error_out| unsafe {
569 ffi::la_environment::la_environment_mechanism_companion_get_state_hash(
570 self.handle.as_ptr(),
571 out,
572 out_len,
573 error_out,
574 )
575 })
576 }
577}
578
579#[derive(Debug)]
581pub struct LAEnvironmentMechanismUserPassword {
582 handle: OwnedHandle,
583}
584
585impl LAEnvironmentMechanismUserPassword {
586 pub(crate) fn from_raw(raw: NonNull<c_void>) -> Self {
587 Self {
588 handle: OwnedHandle::new(raw, ffi::la_environment::la_environment_mechanism_release),
589 }
590 }
591
592 #[allow(dead_code)]
593 pub(crate) const fn as_ptr(&self) -> *mut c_void {
594 self.handle.as_ptr()
595 }
596
597 pub fn is_usable(&self) -> Result<bool> {
603 mechanism_is_usable(self.handle.as_ptr())
604 }
605
606 pub fn localized_name(&self) -> Result<String> {
612 mechanism_localized_name(self.handle.as_ptr())
613 }
614
615 pub fn icon_system_name(&self) -> Result<String> {
621 mechanism_icon_system_name(self.handle.as_ptr())
622 }
623
624 pub fn is_set(&self) -> Result<bool> {
630 bridge_bool(|out, error_out| unsafe {
631 ffi::la_environment::la_environment_mechanism_user_password_get_is_set(
632 self.handle.as_ptr(),
633 out,
634 error_out,
635 )
636 })
637 }
638}