feat(flutter): TTL-desync toggle in the Android profile editor

Mirrors the desktop toggle: a per-profile ttlDesync flag on OstpProfile, a
SwitchListTile in the profile editor (shown for all transports, since it is the
UDP-path counterpart to the UoT-only junk/fragmentation block), and
transport.ttl_desync in the config handed to the engine, with ttl_desync_auto
true so the shared ostp-client measures the hop distance and aims the decoys
itself. The auto-calibration engine is shared, so Android already had the
capability — this just exposes the switch.
This commit is contained in:
ospab 2026-08-19 21:08:24 +03:00
parent bfd079ff48
commit f59d70778a
3 changed files with 16 additions and 0 deletions

View File

@ -23,6 +23,7 @@ class OstpProfile {
int junkPcMax; int junkPcMax;
int junkPsMin; int junkPsMin;
int junkPsMax; int junkPsMax;
bool ttlDesync; // TTL-desync decoys (UDP), auto-calibrated in the engine
OstpProfile({ OstpProfile({
required this.id, required this.id,
@ -38,6 +39,7 @@ class OstpProfile {
this.junkPcMax = 5, this.junkPcMax = 5,
this.junkPsMin = 100, this.junkPsMin = 100,
this.junkPsMax = 1000, this.junkPsMax = 1000,
this.ttlDesync = false,
}); });
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -55,6 +57,7 @@ class OstpProfile {
'junkPcMax': junkPcMax, 'junkPcMax': junkPcMax,
'junkPsMin': junkPsMin, 'junkPsMin': junkPsMin,
'junkPsMax': junkPsMax, 'junkPsMax': junkPsMax,
'ttlDesync': ttlDesync,
}; };
} }
@ -73,6 +76,7 @@ class OstpProfile {
junkPcMax: json['junkPcMax'] as int? ?? 5, junkPcMax: json['junkPcMax'] as int? ?? 5,
junkPsMin: json['junkPsMin'] as int? ?? 100, junkPsMin: json['junkPsMin'] as int? ?? 100,
junkPsMax: json['junkPsMax'] as int? ?? 1000, junkPsMax: json['junkPsMax'] as int? ?? 1000,
ttlDesync: json['ttlDesync'] as bool? ?? false,
); );
} }
} }

View File

@ -138,6 +138,8 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
"frag_sleep": p?.fragSleep ?? 2, "frag_sleep": p?.fragSleep ?? 2,
"junk_pc": [p?.junkPcMin ?? 2, p?.junkPcMax ?? 5], "junk_pc": [p?.junkPcMin ?? 2, p?.junkPcMax ?? 5],
"junk_ps": [p?.junkPsMin ?? 100, p?.junkPsMax ?? 1000], "junk_ps": [p?.junkPsMin ?? 100, p?.junkPsMax ?? 1000],
"ttl_desync": p?.ttlDesync ?? false,
"ttl_desync_auto": true,
}, },
"multiplex": { "multiplex": {
"enabled": muxEnabled, "enabled": muxEnabled,

View File

@ -224,6 +224,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
final junkPsMaxCtrl = TextEditingController(text: (profile?.junkPsMax ?? 1000).toString()); final junkPsMaxCtrl = TextEditingController(text: (profile?.junkPsMax ?? 1000).toString());
String transportMode = profile?.transportMode ?? 'udp'; String transportMode = profile?.transportMode ?? 'udp';
bool tcpFragmentation = profile?.tcpFragmentation ?? false; bool tcpFragmentation = profile?.tcpFragmentation ?? false;
bool ttlDesync = profile?.ttlDesync ?? false;
bool obscureKey = true; bool obscureKey = true;
showDialog( showDialog(
@ -301,6 +302,13 @@ class _SettingsScreenState extends State<SettingsScreen> {
], ],
), ),
], ],
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: const Text('TTL Desync', style: TextStyle(fontSize: 14)),
subtitle: const Text('Decoy packets that die before the server (UDP, auto-tuned)', style: TextStyle(fontSize: 12, color: Colors.white54)),
value: ttlDesync,
onChanged: (v) => setDialogState(() => ttlDesync = v),
),
], ],
), ),
), ),
@ -348,6 +356,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
junkPcMax: int.tryParse(junkPcMaxCtrl.text) ?? 5, junkPcMax: int.tryParse(junkPcMaxCtrl.text) ?? 5,
junkPsMin: int.tryParse(junkPsMinCtrl.text) ?? 100, junkPsMin: int.tryParse(junkPsMinCtrl.text) ?? 100,
junkPsMax: int.tryParse(junkPsMaxCtrl.text) ?? 1000, junkPsMax: int.tryParse(junkPsMaxCtrl.text) ?? 1000,
ttlDesync: ttlDesync,
)); ));
} else { } else {
profile.name = nameCtrl.text.trim().isNotEmpty ? nameCtrl.text.trim() : server; profile.name = nameCtrl.text.trim().isNotEmpty ? nameCtrl.text.trim() : server;
@ -361,6 +370,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
profile.junkPcMax = int.tryParse(junkPcMaxCtrl.text) ?? 5; profile.junkPcMax = int.tryParse(junkPcMaxCtrl.text) ?? 5;
profile.junkPsMin = int.tryParse(junkPsMinCtrl.text) ?? 100; profile.junkPsMin = int.tryParse(junkPsMinCtrl.text) ?? 100;
profile.junkPsMax = int.tryParse(junkPsMaxCtrl.text) ?? 1000; profile.junkPsMax = int.tryParse(junkPsMaxCtrl.text) ?? 1000;
profile.ttlDesync = ttlDesync;
} }
_saveProfiles(); _saveProfiles();
}); });