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 let _ = catch_unwind(AssertUnwindSafe(|| unsafe {
86 drop(Box::from_raw(context.as_ptr()));
87 }));
88 }
89}
90
91#[derive(Debug)]
93pub struct LAEnvironmentObserverRegistration {
94 handle: OwnedHandle,
95}
96
97impl LAEnvironmentObserverRegistration {
98 pub(crate) const fn as_ptr(&self) -> *mut c_void {
99 self.handle.as_ptr()
100 }
101}
102
103#[derive(Debug)]
105pub struct LAEnvironment {
106 handle: OwnedHandle,
107}
108
109impl LAEnvironment {
110 pub(crate) fn from_raw(raw: NonNull<c_void>) -> Self {
111 Self {
112 handle: OwnedHandle::new(raw, ffi::la_environment::la_environment_release),
113 }
114 }
115
116 #[allow(dead_code)]
117 pub(crate) const fn as_ptr(&self) -> *mut c_void {
118 self.handle.as_ptr()
119 }
120
121 pub fn current_user() -> Result<Self> {
127 Ok(Self::from_raw(bridge_ptr(|out, error_out| unsafe {
128 ffi::la_environment::la_environment_current_user(out, error_out)
129 })?))
130 }
131
132 pub fn state(&self) -> Result<LAEnvironmentState> {
138 Ok(LAEnvironmentState::from_raw(bridge_ptr(
139 |out, error_out| unsafe {
140 ffi::la_environment::la_environment_get_state(self.handle.as_ptr(), out, error_out)
141 },
142 )?))
143 }
144
145 pub fn add_observer<O>(&self, observer: O) -> Result<LAEnvironmentObserverRegistration>
154 where
155 O: LAEnvironmentObserver,
156 {
157 let context = Box::new(EnvironmentObserverContext {
158 observer: Box::new(observer),
159 });
160 let context_ptr = Box::into_raw(context).cast::<c_void>();
161
162 let observer_raw = match bridge_ptr(|out, error_out| unsafe {
163 ffi::la_environment::la_environment_observer_new(
164 Some(environment_observer_trampoline),
165 Some(environment_observer_release),
166 context_ptr,
167 out,
168 error_out,
169 )
170 }) {
171 Ok(raw) => raw,
172 Err(error) => {
173 unsafe { environment_observer_release(context_ptr) };
174 return Err(error);
175 }
176 };
177
178 let registration = LAEnvironmentObserverRegistration {
179 handle: OwnedHandle::new(
180 observer_raw,
181 ffi::la_environment::la_environment_observer_release,
182 ),
183 };
184
185 if let Err(error) = bridge_unit(|error_out| unsafe {
186 ffi::la_environment::la_environment_add_observer(
187 self.handle.as_ptr(),
188 registration.handle.as_ptr(),
189 error_out,
190 )
191 }) {
192 drop(registration);
193 return Err(error);
194 }
195
196 Ok(registration)
197 }
198
199 pub fn remove_observer(&self, observer: &LAEnvironmentObserverRegistration) -> Result<()> {
205 bridge_unit(|error_out| unsafe {
206 ffi::la_environment::la_environment_remove_observer(
207 self.handle.as_ptr(),
208 observer.as_ptr(),
209 error_out,
210 )
211 })
212 }
213}
214
215#[derive(Debug)]
217pub struct LAEnvironmentState {
218 handle: OwnedHandle,
219}
220
221impl LAEnvironmentState {
222 pub(crate) fn from_raw(raw: NonNull<c_void>) -> Self {
223 Self {
224 handle: OwnedHandle::new(raw, ffi::la_environment::la_environment_state_release),
225 }
226 }
227
228 #[allow(dead_code)]
229 pub(crate) const fn as_ptr(&self) -> *mut c_void {
230 self.handle.as_ptr()
231 }
232
233 pub fn biometry(&self) -> Result<Option<LAEnvironmentMechanismBiometry>> {
239 let raw = bridge_opt_ptr(|out, error_out| unsafe {
240 ffi::la_environment::la_environment_state_get_biometry(self.handle.as_ptr(), out, error_out)
241 })?;
242 Ok(raw.map(LAEnvironmentMechanismBiometry::from_raw))
243 }
244
245 pub fn user_password(&self) -> Result<Option<LAEnvironmentMechanismUserPassword>> {
251 let raw = bridge_opt_ptr(|out, error_out| unsafe {
252 ffi::la_environment::la_environment_state_get_user_password(
253 self.handle.as_ptr(),
254 out,
255 error_out,
256 )
257 })?;
258 Ok(raw.map(LAEnvironmentMechanismUserPassword::from_raw))
259 }
260
261 pub fn companions(&self) -> Result<Vec<LAEnvironmentMechanismCompanion>> {
267 let count = count_to_usize(
268 bridge_i64(|out, error_out| unsafe {
269 ffi::la_environment::la_environment_state_get_companion_count(
270 self.handle.as_ptr(),
271 out,
272 error_out,
273 )
274 })?,
275 "companion",
276 )?;
277
278 let mut mechanisms = Vec::with_capacity(count);
279 for index in 0..count {
280 let index = i64::try_from(index).map_err(|_| {
281 LAError::BridgeFailed(
282 "LocalAuthentication returned more companion mechanisms than this platform can index"
283 .to_owned(),
284 )
285 })?;
286 let raw = bridge_ptr(|out, error_out| unsafe {
287 ffi::la_environment::la_environment_state_get_companion_at(
288 self.handle.as_ptr(),
289 index,
290 out,
291 error_out,
292 )
293 })?;
294 mechanisms.push(LAEnvironmentMechanismCompanion::from_raw(raw));
295 }
296 Ok(mechanisms)
297 }
298
299 pub fn all_mechanisms(&self) -> Result<Vec<LAEnvironmentMechanism>> {
305 let count = count_to_usize(
306 bridge_i64(|out, error_out| unsafe {
307 ffi::la_environment::la_environment_state_get_all_mechanism_count(
308 self.handle.as_ptr(),
309 out,
310 error_out,
311 )
312 })?,
313 "mechanism",
314 )?;
315
316 let mut mechanisms = Vec::with_capacity(count);
317 for index in 0..count {
318 let index = i64::try_from(index).map_err(|_| {
319 LAError::BridgeFailed(
320 "LocalAuthentication returned more mechanisms than this platform can index"
321 .to_owned(),
322 )
323 })?;
324 let raw = bridge_ptr(|out, error_out| unsafe {
325 ffi::la_environment::la_environment_state_get_all_mechanism_at(
326 self.handle.as_ptr(),
327 index,
328 out,
329 error_out,
330 )
331 })?;
332 mechanisms.push(LAEnvironmentMechanism::from_raw(raw));
333 }
334 Ok(mechanisms)
335 }
336}
337
338#[derive(Debug)]
340pub struct LAEnvironmentMechanism {
341 handle: OwnedHandle,
342}
343
344impl LAEnvironmentMechanism {
345 pub(crate) fn from_raw(raw: NonNull<c_void>) -> Self {
346 Self {
347 handle: OwnedHandle::new(raw, ffi::la_environment::la_environment_mechanism_release),
348 }
349 }
350
351 #[allow(dead_code)]
352 pub(crate) const fn as_ptr(&self) -> *mut c_void {
353 self.handle.as_ptr()
354 }
355
356 pub fn is_usable(&self) -> Result<bool> {
362 mechanism_is_usable(self.handle.as_ptr())
363 }
364
365 pub fn localized_name(&self) -> Result<String> {
371 mechanism_localized_name(self.handle.as_ptr())
372 }
373
374 pub fn icon_system_name(&self) -> Result<String> {
380 mechanism_icon_system_name(self.handle.as_ptr())
381 }
382}
383
384#[derive(Debug)]
386pub struct LAEnvironmentMechanismBiometry {
387 handle: OwnedHandle,
388}
389
390impl LAEnvironmentMechanismBiometry {
391 pub(crate) fn from_raw(raw: NonNull<c_void>) -> Self {
392 Self {
393 handle: OwnedHandle::new(raw, ffi::la_environment::la_environment_mechanism_release),
394 }
395 }
396
397 #[allow(dead_code)]
398 pub(crate) const fn as_ptr(&self) -> *mut c_void {
399 self.handle.as_ptr()
400 }
401
402 pub fn is_usable(&self) -> Result<bool> {
408 mechanism_is_usable(self.handle.as_ptr())
409 }
410
411 pub fn localized_name(&self) -> Result<String> {
417 mechanism_localized_name(self.handle.as_ptr())
418 }
419
420 pub fn icon_system_name(&self) -> Result<String> {
426 mechanism_icon_system_name(self.handle.as_ptr())
427 }
428
429 pub fn biometry_type(&self) -> Result<BiometryType> {
435 Ok(BiometryType::from_ffi(bridge_i32(|out, error_out| unsafe {
436 ffi::la_environment::la_environment_mechanism_biometry_get_biometry_type(
437 self.handle.as_ptr(),
438 out,
439 error_out,
440 )
441 })?))
442 }
443
444 pub fn is_enrolled(&self) -> Result<bool> {
450 bridge_bool(|out, error_out| unsafe {
451 ffi::la_environment::la_environment_mechanism_biometry_get_is_enrolled(
452 self.handle.as_ptr(),
453 out,
454 error_out,
455 )
456 })
457 }
458
459 pub fn is_locked_out(&self) -> Result<bool> {
465 bridge_bool(|out, error_out| unsafe {
466 ffi::la_environment::la_environment_mechanism_biometry_get_is_locked_out(
467 self.handle.as_ptr(),
468 out,
469 error_out,
470 )
471 })
472 }
473
474 pub fn state_hash(&self) -> Result<Vec<u8>> {
480 bridge_bytes(|out, out_len, error_out| unsafe {
481 ffi::la_environment::la_environment_mechanism_biometry_get_state_hash(
482 self.handle.as_ptr(),
483 out,
484 out_len,
485 error_out,
486 )
487 })
488 }
489
490 pub fn built_in_sensor_inaccessible(&self) -> Result<bool> {
496 bridge_bool(|out, error_out| unsafe {
497 ffi::la_environment::la_environment_mechanism_biometry_get_built_in_sensor_inaccessible(
498 self.handle.as_ptr(),
499 out,
500 error_out,
501 )
502 })
503 }
504}
505
506#[derive(Debug)]
508pub struct LAEnvironmentMechanismCompanion {
509 handle: OwnedHandle,
510}
511
512impl LAEnvironmentMechanismCompanion {
513 pub(crate) fn from_raw(raw: NonNull<c_void>) -> Self {
514 Self {
515 handle: OwnedHandle::new(raw, ffi::la_environment::la_environment_mechanism_release),
516 }
517 }
518
519 #[allow(dead_code)]
520 pub(crate) const fn as_ptr(&self) -> *mut c_void {
521 self.handle.as_ptr()
522 }
523
524 pub fn is_usable(&self) -> Result<bool> {
530 mechanism_is_usable(self.handle.as_ptr())
531 }
532
533 pub fn localized_name(&self) -> Result<String> {
539 mechanism_localized_name(self.handle.as_ptr())
540 }
541
542 pub fn icon_system_name(&self) -> Result<String> {
548 mechanism_icon_system_name(self.handle.as_ptr())
549 }
550
551 pub fn companion_type(&self) -> Result<LACompanionType> {
557 Ok(LACompanionType::from_ffi(bridge_i32(|out, error_out| unsafe {
558 ffi::la_environment::la_environment_mechanism_companion_get_type(
559 self.handle.as_ptr(),
560 out,
561 error_out,
562 )
563 })?))
564 }
565
566 pub fn state_hash(&self) -> Result<Option<Vec<u8>>> {
572 bridge_opt_bytes(|out, out_len, error_out| unsafe {
573 ffi::la_environment::la_environment_mechanism_companion_get_state_hash(
574 self.handle.as_ptr(),
575 out,
576 out_len,
577 error_out,
578 )
579 })
580 }
581}
582
583#[derive(Debug)]
585pub struct LAEnvironmentMechanismUserPassword {
586 handle: OwnedHandle,
587}
588
589impl LAEnvironmentMechanismUserPassword {
590 pub(crate) fn from_raw(raw: NonNull<c_void>) -> Self {
591 Self {
592 handle: OwnedHandle::new(raw, ffi::la_environment::la_environment_mechanism_release),
593 }
594 }
595
596 #[allow(dead_code)]
597 pub(crate) const fn as_ptr(&self) -> *mut c_void {
598 self.handle.as_ptr()
599 }
600
601 pub fn is_usable(&self) -> Result<bool> {
607 mechanism_is_usable(self.handle.as_ptr())
608 }
609
610 pub fn localized_name(&self) -> Result<String> {
616 mechanism_localized_name(self.handle.as_ptr())
617 }
618
619 pub fn icon_system_name(&self) -> Result<String> {
625 mechanism_icon_system_name(self.handle.as_ptr())
626 }
627
628 pub fn is_set(&self) -> Result<bool> {
634 bridge_bool(|out, error_out| unsafe {
635 ffi::la_environment::la_environment_mechanism_user_password_get_is_set(
636 self.handle.as_ptr(),
637 out,
638 error_out,
639 )
640 })
641 }
642}