fix(android): set the biometric strengths before asking what the device can do
A pattern lock reported "no authentication here", so the cache was written under the plain key. FMX builds its authenticator mask from FBiometricStrengths, and that field starts as the EMPTY SET - the constructor never initialises it. On API 30+ CanAuthenticate therefore calls canAuthenticate(0), which never answers "available", whatever the device can actually do. Asking before setting the strengths cannot succeed. Both entry points now go through AcquireService, which sets Strong + DeviceCredential and only then hands back the service. DeviceCredential is the point: a phone with a pattern or a PIN and no enrolled fingerprint is the common case, not an edge one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,28 @@ implementation
|
|||||||
{$IF DEFINED(ANDROID)}
|
{$IF DEFINED(ANDROID)}
|
||||||
uses
|
uses
|
||||||
FMX.BiometricAuth, FMX.Platform;
|
FMX.BiometricAuth, FMX.Platform;
|
||||||
|
|
||||||
|
const
|
||||||
|
// DeviceCredential inclus : un telephone protege par un schema ou un code,
|
||||||
|
// sans empreinte enrolee, doit rester utilisable. C'est le cas courant.
|
||||||
|
WANTED_STRENGTHS = [TBiometricStrength.Strong,
|
||||||
|
TBiometricStrength.DeviceCredential];
|
||||||
|
|
||||||
|
/// Recupere le service ET pose les forces d'authentification.
|
||||||
|
///
|
||||||
|
/// L'ORDRE N'EST PAS COSMETIQUE. Le service Android construit son masque
|
||||||
|
/// d'authentificateurs a partir de ces forces, et son champ demarre a
|
||||||
|
/// l'ensemble VIDE - le constructeur ne l'initialise pas. Interroger
|
||||||
|
/// CanAuthenticate avant de les poser revient donc a demander
|
||||||
|
/// canAuthenticate(0), qui ne repond jamais « disponible », quel que soit ce
|
||||||
|
/// que l'appareil sait faire.
|
||||||
|
function AcquireService(out ASvc: IFMXBiometricAuthService): Boolean;
|
||||||
|
begin
|
||||||
|
Result := TPlatformServices.Current.SupportsPlatformService(
|
||||||
|
IFMXBiometricAuthService, ASvc);
|
||||||
|
if Result then
|
||||||
|
ASvc.SetBiometricStrengths(WANTED_STRENGTHS);
|
||||||
|
end;
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
|
|
||||||
function BiometricAvailable: Boolean;
|
function BiometricAvailable: Boolean;
|
||||||
@@ -53,8 +75,7 @@ begin
|
|||||||
// pas par « pas de biometrie » mais par une boite d'exception en pleine
|
// pas par « pas de biometrie » mais par une boite d'exception en pleine
|
||||||
// ouverture du coffre.
|
// ouverture du coffre.
|
||||||
try
|
try
|
||||||
Result := TPlatformServices.Current.SupportsPlatformService(
|
Result := AcquireService(LSvc) and LSvc.CanAuthenticate;
|
||||||
IFMXBiometricAuthService, LSvc) and LSvc.CanAuthenticate;
|
|
||||||
except
|
except
|
||||||
Result := False;
|
Result := False;
|
||||||
end;
|
end;
|
||||||
@@ -73,27 +94,28 @@ var
|
|||||||
begin
|
begin
|
||||||
if not Assigned(AThen) then Exit;
|
if not Assigned(AThen) then Exit;
|
||||||
|
|
||||||
if not BiometricAvailable then
|
// AcquireService pose les forces ; CanAuthenticate n'a de sens qu'apres.
|
||||||
|
if not AcquireService(LSvc) then
|
||||||
begin
|
begin
|
||||||
AThen(boUnavailable);
|
AThen(boUnavailable);
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
if not TPlatformServices.Current.SupportsPlatformService(
|
try
|
||||||
IFMXBiometricAuthService, LSvc) then
|
if not LSvc.CanAuthenticate then
|
||||||
begin
|
begin
|
||||||
AThen(boUnavailable);
|
AThen(boUnavailable);
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
|
except
|
||||||
|
AThen(boUnavailable);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
LProps := Default(TBiometricPromptProperties);
|
LProps := Default(TBiometricPromptProperties);
|
||||||
LProps.Title := 'Deverrouiller le coffre';
|
LProps.Title := 'Deverrouiller le coffre';
|
||||||
LProps.Description := APrompt;
|
LProps.Description := APrompt;
|
||||||
LProps.CancelButtonText := 'Annuler';
|
LProps.CancelButtonText := 'Annuler';
|
||||||
LSvc.SetPromptProperties(LProps);
|
LSvc.SetPromptProperties(LProps);
|
||||||
// DeviceCredential inclus : un telephone sans empreinte enrolee mais avec
|
|
||||||
// un code de verrouillage reste utilisable, au lieu d'etre bloque.
|
|
||||||
LSvc.SetBiometricStrengths([TBiometricStrength.Strong,
|
|
||||||
TBiometricStrength.DeviceCredential]);
|
|
||||||
|
|
||||||
// Meme raison que plus haut : ce qui remonte de la couche Java doit
|
// Meme raison que plus haut : ce qui remonte de la couche Java doit
|
||||||
// ressortir en TBioOutcome, jamais en exception - l'appelant a un chemin
|
// ressortir en TBioOutcome, jamais en exception - l'appelant a un chemin
|
||||||
|
|||||||
Reference in New Issue
Block a user