fix(android): declare FMX's biometric activity, and stop LoadVault from deleting the cache
The prompt raised ActivityNotFoundException: FMX does not show BiometricPrompt from our NativeActivity, it launches its own BiometricFragmentActivity, which lives in fmx.dex.jar but has to be declared in the manifest. Given a translucent framework theme - it hosts the system dialog and draws nothing itself, and the generated resources contain no AppCompat. Then the read failed and the cache vanished with no trace. LoadVault sorted 'key lost' from 'not authenticated' on the exception message and deleted the file when it guessed the former - so a wrong guess destroyed the cache and the only record of why. It now reports through LastStoreError and deletes nothing; the caller has the log and the context. OpenLocalNow shows the reason, after ShowUnlock rather than before, which was overwriting it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -77,6 +77,21 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- FMX n'affiche pas BiometricPrompt depuis notre NativeActivity : il
|
||||
lance SA PROPRE activite, qui heberge le fragment androidx. Elle
|
||||
vit dans fmx.dex.jar, mais Android exige qu'elle soit declaree ici
|
||||
- sinon android.content.ActivityNotFoundException a la premiere
|
||||
invite, et nulle part avant.
|
||||
|
||||
Theme translucide du framework : cette activite ne dessine rien
|
||||
d'elle-meme, elle ne fait qu'heberger le dialogue systeme. Sans
|
||||
cela elle recouvrirait l'ecran d'une surface opaque le temps de
|
||||
l'authentification. On prend un theme @android: et non AppCompat,
|
||||
absent des ressources generees. -->
|
||||
<activity
|
||||
android:name="com.embarcadero.firemonkey.biometricauth.BiometricFragmentActivity"
|
||||
android:exported="false"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
||||
<%activity%>
|
||||
<%receivers%>
|
||||
</application>
|
||||
|
||||
@@ -92,7 +92,16 @@ function VaultNeedsAuth: Boolean;
|
||||
/// range : l'appelant redemande alors, et le prochain enregistrement le pose.
|
||||
function LoadVault(out APlainJson, AETag, ASyncPwd: string): Boolean;
|
||||
|
||||
/// Efface le coffre local. A appeler au moindre doute : il se retelecharge.
|
||||
/// Pourquoi la derniere lecture a echoue. Vide apres un succes.
|
||||
///
|
||||
/// LoadVault N'EFFACE RIEN de lui-meme. Un cache detruit par une routine de
|
||||
/// lecture, c'est un diagnostic errone qui coute un retelechargement et
|
||||
/// 4,5 s de derivation - et qui emporte la cause avec lui. Elle rapporte,
|
||||
/// l'appelant decide.
|
||||
function LastStoreError: string;
|
||||
|
||||
/// Efface le coffre local. Seul un geste explicite de l'utilisateur, ou une
|
||||
/// certitude, devrait l'appeler : il se retelecharge, mais pas gratuitement.
|
||||
procedure ClearVault;
|
||||
|
||||
/// Chemin du fichier, pour le journal et les tests.
|
||||
@@ -129,6 +138,14 @@ const
|
||||
VER_WRAP = 3; // AES aleatoire enveloppee par la cle publique RSA
|
||||
RSA_TRANSFORM = 'RSA/ECB/OAEPWithSHA-256AndMGF1Padding';
|
||||
|
||||
var
|
||||
FLastStoreError: string = '';
|
||||
|
||||
function LastStoreError: string;
|
||||
begin
|
||||
Result := FLastStoreError;
|
||||
end;
|
||||
|
||||
function VaultPath: string;
|
||||
begin
|
||||
// GetHomePath sur Android = /data/data/<paquet>/files, prive a l'app.
|
||||
@@ -678,6 +695,7 @@ begin
|
||||
APlainJson := '';
|
||||
AETag := '';
|
||||
ASyncPwd := '';
|
||||
FLastStoreError := '';
|
||||
if not TFile.Exists(VaultPath) then Exit;
|
||||
|
||||
try
|
||||
@@ -743,18 +761,11 @@ begin
|
||||
on E: Exception do
|
||||
begin
|
||||
Result := False;
|
||||
// NE PAS effacer sur un simple defaut d'authentification : le coffre est
|
||||
// intact, c'est l'utilisateur qui n'a pas presente son schema ou qui a
|
||||
// laisse expirer le delai. L'effacer imposerait un retelechargement et
|
||||
// 4,5 secondes de derivation pour une empreinte annulee.
|
||||
//
|
||||
// On efface en revanche quand la cle est REELLEMENT perdue
|
||||
// (reinstallation, Keystore vide, nouvelle empreinte enrolee qui
|
||||
// invalide la cle) ou quand le fichier est altere : dans ces cas il ne
|
||||
// redeviendra jamais lisible.
|
||||
if (Pos('UserNotAuthenticated', E.Message) = 0) and
|
||||
(Pos('NotAuthenticated', E.ClassName) = 0) then
|
||||
ClearVault;
|
||||
// ON N'EFFACE PAS. Le tri « cle perdue / pas authentifie » se faisait
|
||||
// ici sur le texte du message, et une erreur de tri detruisait le cache
|
||||
// en emportant la seule trace de sa cause. L'appelant a le journal et le
|
||||
// contexte - lui seul sait si l'utilisateur a demande quelque chose.
|
||||
FLastStoreError := E.ClassName + ': ' + E.Message;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
+4
-1
@@ -727,8 +727,11 @@ begin
|
||||
end
|
||||
else
|
||||
begin
|
||||
Status('Coffre local illisible - retelechargement necessaire');
|
||||
// ShowUnlock AVANT le message : il repositionne le statut selon ce que
|
||||
// l'appareil detient, et ecraserait donc l'explication.
|
||||
ShowUnlock;
|
||||
Log('Coffre local illisible : ' + LastStoreError);
|
||||
Status('Coffre local illisible - voir le journal');
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user