fix(android): opening the vault no longer calls the server every time
Asked plainly by the user: why does open trigger a sync? Because OpenLocalNow displays the cache and then refreshes in the background, so a change made on the desktop shows up without anyone thinking about it. Sound intent, applied on EVERY open - including a lock followed by a re-open three seconds later, which cannot learn anything new. Measured cost of one such pointless refresh: 1 s of download and 5.5 s of key derivation. A minimum interval between two background refreshes fixes that, whatever else is wrong: the desktop does not publish several times a minute, and looking at most once a quarter of an hour is enough for a change to arrive unnoticed. Pressing Sync goes through another path and never waits. The sha comparison that should already have made an unchanged refresh cheap is still not firing - two fingerprints print identically yet compare unequal. It is now compared with SameText on trimmed values, which cannot confuse two distinct snapshots since a hex digest never differs by case or whitespace alone, and the log reports both lengths so the invisible character shows up on the next real refresh. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -207,7 +207,15 @@ begin
|
|||||||
// Meme sha : le contenu est celui qu'on detient deja. On rend la
|
// Meme sha : le contenu est celui qu'on detient deja. On rend la
|
||||||
// meme reponse qu'un 304 pour que l'appelant epargne la derivation,
|
// meme reponse qu'un 304 pour que l'appelant epargne la derivation,
|
||||||
// et on jette le conteneur - il ne servirait a rien.
|
// et on jette le conteneur - il ne servirait a rien.
|
||||||
else if (AKnownETag <> '') and (Result.ETag = AKnownETag) then
|
//
|
||||||
|
// COMPARAISON INSENSIBLE A LA CASSE ET AUX BLANCS. Une empreinte
|
||||||
|
// hexadecimale ne peut pas differer seulement par la casse ou par
|
||||||
|
// un espace : trancher ainsi ne peut donc pas confondre deux
|
||||||
|
// instantanes distincts, alors qu'une egalite stricte se laisse
|
||||||
|
// battre par un caractere invisible - un guillemet d'en-tete, une
|
||||||
|
// fin de ligne - et fait payer une derivation complete pour rien.
|
||||||
|
else if (AKnownETag <> '') and
|
||||||
|
SameText(Result.ETag.Trim, AKnownETag.Trim) then
|
||||||
begin
|
begin
|
||||||
Result.Status := ssNotModified;
|
Result.Status := ssNotModified;
|
||||||
Result.Container := '';
|
Result.Container := '';
|
||||||
|
|||||||
+34
-1
@@ -72,6 +72,19 @@ const
|
|||||||
// qu'un telephone repose ouvert.
|
// qu'un telephone repose ouvert.
|
||||||
BACKGROUND_GRACE_SECONDS = 30;
|
BACKGROUND_GRACE_SECONDS = 30;
|
||||||
|
|
||||||
|
// Duree minimale entre deux rafraichissements de fond.
|
||||||
|
//
|
||||||
|
// L'ouverture du coffre en declenchait un CHAQUE FOIS. Verrouiller puis
|
||||||
|
// rouvrir trois secondes plus tard rappelait donc le serveur, qui ne pouvait
|
||||||
|
// rien apprendre de neuf : un telechargement complet, et une derivation de
|
||||||
|
// cle entiere des que l'empreinte ne concordait pas. Le poste ne publie pas
|
||||||
|
// plusieurs fois par minute ; consulter au plus une fois par quart d'heure
|
||||||
|
// suffit a ce que la modification arrive sans qu'on y pense.
|
||||||
|
//
|
||||||
|
// Ne concerne QUE le rafraichissement automatique. Un appui sur Sync passe
|
||||||
|
// par un autre chemin et n'attend jamais.
|
||||||
|
REFRESH_MIN_MINUTES = 15;
|
||||||
|
|
||||||
// FIRSTRUN_TOKEN et TEST_PWD viennent d'un fichier LOCAL, hors depot : le
|
// FIRSTRUN_TOKEN et TEST_PWD viennent d'un fichier LOCAL, hors depot : le
|
||||||
// jeton authentifie l'appareil, le mot de passe ouvre le coffre entier. Ni
|
// jeton authentifie l'appareil, le mot de passe ouvre le coffre entier. Ni
|
||||||
// l'un ni l'autre n'a sa place dans un depot, meme sans remote. Absents apres
|
// l'un ni l'autre n'a sa place dans un depot, meme sans remote. Absents apres
|
||||||
@@ -135,6 +148,8 @@ type
|
|||||||
FLastActivity: TDateTime;
|
FLastActivity: TDateTime;
|
||||||
/// Quand l'application est passee derriere. 0 = elle est au premier plan.
|
/// Quand l'application est passee derriere. 0 = elle est au premier plan.
|
||||||
FBackgroundAt: TDateTime;
|
FBackgroundAt: TDateTime;
|
||||||
|
/// Dernier rafraichissement de fond. 0 = jamais, donc le premier a lieu.
|
||||||
|
FLastRefresh: TDateTime;
|
||||||
procedure Touch;
|
procedure Touch;
|
||||||
procedure OnIdleTimer(Sender: TObject);
|
procedure OnIdleTimer(Sender: TObject);
|
||||||
|
|
||||||
@@ -629,8 +644,17 @@ begin
|
|||||||
// Afficher d'abord, synchroniser ensuite : le coffre est a l'ecran, le
|
// Afficher d'abord, synchroniser ensuite : le coffre est a l'ecran, le
|
||||||
// reseau se debrouille derriere. Sans mot de passe range, on ne peut
|
// reseau se debrouille derriere. Sans mot de passe range, on ne peut
|
||||||
// rien ouvrir de nouveau - inutile d'aller voir.
|
// rien ouvrir de nouveau - inutile d'aller voir.
|
||||||
if FSyncPwd <> '' then
|
if FSyncPwd = '' then
|
||||||
|
Log(' no stored sync password - no background refresh')
|
||||||
|
else if (FLastRefresh > 0) and
|
||||||
|
(MinutesBetween(Now, FLastRefresh) < REFRESH_MIN_MINUTES) then
|
||||||
|
Log(Format(' refreshed %d min ago - skipping (min %d)',
|
||||||
|
[MinutesBetween(Now, FLastRefresh), REFRESH_MIN_MINUTES]))
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
FLastRefresh := Now;
|
||||||
FetchAndDecrypt(FUrl, FToken, FSyncPwd, True);
|
FetchAndDecrypt(FUrl, FToken, FSyncPwd, True);
|
||||||
|
end;
|
||||||
except
|
except
|
||||||
on E: Exception do
|
on E: Exception do
|
||||||
begin
|
begin
|
||||||
@@ -880,6 +904,14 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
Log(' received ' + Length(LRes.Container).ToString + ' bytes');
|
Log(' received ' + Length(LRes.Container).ToString + ' bytes');
|
||||||
|
// LES DEUX EMPREINTES COTE A COTE. Sans cela on ne peut pas trancher
|
||||||
|
// entre « le distant a change » et « la comparaison ne se declenche
|
||||||
|
// pas », et les deux se ressemblent parfaitement de l'exterieur.
|
||||||
|
// LES LONGUEURS AUTANT QUE LES VALEURS. Deux empreintes s'affichaient
|
||||||
|
// identiques alors que l'egalite les separait : seul un caractere
|
||||||
|
// invisible peut faire cela, et il ne se voit qu'au compte.
|
||||||
|
Log(Format(' remote sha: %s (%d chars, known %d)',
|
||||||
|
[LRes.ETag, Length(LRes.ETag), Length(FETag)]));
|
||||||
|
|
||||||
// LES COORDONNEES SONT RETENUES ICI, avant le dechiffrement. Un 200
|
// LES COORDONNEES SONT RETENUES ICI, avant le dechiffrement. Un 200
|
||||||
// avec un conteneur en main prouve l'adresse ET le jeton ; le mot de
|
// avec un conteneur en main prouve l'adresse ET le jeton ; le mot de
|
||||||
@@ -915,6 +947,7 @@ begin
|
|||||||
// Le mot de passe part avec le coffre : c'est lui qui permettra au
|
// Le mot de passe part avec le coffre : c'est lui qui permettra au
|
||||||
// prochain lancement de rafraichir sans rien demander.
|
// prochain lancement de rafraichir sans rien demander.
|
||||||
SaveVault(LPlain, FETag, APwd, LCanAuth);
|
SaveVault(LPlain, FETag, APwd, LCanAuth);
|
||||||
|
Log(' cache now holds sha: ' + FETag);
|
||||||
if VaultNeedsAuth then
|
if VaultNeedsAuth then
|
||||||
Log(' cache written - protected by biometrics/PIN')
|
Log(' cache written - protected by biometrics/PIN')
|
||||||
else if LCanAuth then
|
else if LCanAuth then
|
||||||
|
|||||||
Reference in New Issue
Block a user