fix(sync): recover connection after Android screen lock/unlock
TcpClient.Connected only reflects the last known socket state, so a connection killed silently while the phone was locked (Doze, mobile radio suspend, NAT timeout) still reported IsConnected == true. The keep-alive ping's failure was swallowed by an empty catch, so the stale "connected" state never cleared and sync kept retrying on a dead socket instead of reconnecting. Treat a failed keep-alive ping as a disconnect (tear down the client and reconnect), and add OnPause/OnResume to the Android activity to force an immediate health check on resume instead of waiting for the 20s timer, which may itself be suspended during Doze.
This commit is contained in:
1 parent
bbed21e820
commit
94a474fe41
3 files changed
+57
-3
No files matched your search
@@ -8,6 +8,10 @@ namespace PalladiumWallet.App;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
/// <summary>Set once the single ViewModel is created; lets platform heads (e.g. the
|
||||
/// Android activity) reach it for lifecycle events without a second instance.</summary>
|
||||
public static MainWindowViewModel? MainViewModel { get; private set; }
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
@@ -16,6 +20,7 @@ public partial class App : Application
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
var vm = new MainWindowViewModel();
|
||||
MainViewModel = vm;
|
||||
|
||||
// Desktop (Windows/Linux): classic window. Mobile (Android): single
|
||||
// view. Same shared UI (MainView) and same ViewModel.
|
||||
|
||||
@@ -165,7 +165,7 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
{
|
||||
if (IsSyncing)
|
||||
return;
|
||||
if (_client is { IsConnected: true })
|
||||
if (_client is { IsConnected: true } client)
|
||||
{
|
||||
// If the wallet is open and the last sync failed, retry automatically.
|
||||
if (_syncFailed && _account is not null)
|
||||
@@ -173,8 +173,24 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
await ConnectAndSync();
|
||||
return;
|
||||
}
|
||||
try { await _client.PingAsync(); }
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
await client.PingAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// TcpClient.Connected only reflects the last known socket state, so a
|
||||
// connection killed silently while the app was suspended (e.g. Android
|
||||
// Doze after screen lock) still reports IsConnected == true. A failed
|
||||
// ping is the only reliable signal here: tear the dead client down so
|
||||
// the next tick reconnects instead of retrying forever on a dead socket.
|
||||
await DisconnectAsync();
|
||||
if (_autoReconnect)
|
||||
{
|
||||
ConnectionStatus = Loc.Tr("conn.reconnecting");
|
||||
await ConnectAndSync();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_autoReconnect)
|
||||
{
|
||||
@@ -183,6 +199,17 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Forces an immediate connection health check, bypassing the 20s timer.
|
||||
/// Called when the app resumes from background/lock screen, since the socket may
|
||||
/// have died silently while suspended and the UI would otherwise show a stale
|
||||
/// "connected" state until the next scheduled tick (or forever, if it never fires
|
||||
/// during Doze).</summary>
|
||||
public async System.Threading.Tasks.Task CheckConnectionOnResumeAsync()
|
||||
{
|
||||
if (IsSyncing) return;
|
||||
await KeepAliveTickAsync();
|
||||
}
|
||||
|
||||
// ---- wallet lifecycle ----
|
||||
|
||||
[RelayCommand]
|
||||
|
||||
Reference in new issue
Block a user