This commit is contained in:
2026-08-16 19:02:48 -03:00
parent 96fe00eb2b
commit c8e3011f21
31 changed files with 3457 additions and 351 deletions
@@ -38,7 +38,7 @@ public class DragonService extends VpnService {
public static final String EXTRA_SERVER = "server";
public static final String EXTRA_PORT = "port";
public static final String EXTRA_TOKEN = "token";
/** Wire format: "auto", "b" or "x". */
/** Wire format: "auto", "b", "bp" or "x". */
public static final String EXTRA_WIRE = "wire";
public static final String EXTRA_CHUNK_MAX = "chunkMax";
public static final String EXTRA_CHUNK_MIN = "chunkMin";
@@ -48,6 +48,8 @@ public class DragonService extends VpnService {
public static final String EXTRA_BATCH_MIN = "batchMin";
public static final String EXTRA_RECONNECT = "reconnect";
public static final String EXTRA_TIMEOUT = "timeout";
public static final String EXTRA_PROBE_DELAY = "probeDelay";
public static final String EXTRA_PROBE_THREADS = "probeThreads";
private static final int BATCH_LIMIT = 256;
@@ -118,7 +120,9 @@ public class DragonService extends VpnService {
int batchMax = intent.getIntExtra(EXTRA_BATCH_MAX, 1);
int batchMin = intent.getIntExtra(EXTRA_BATCH_MIN, 1);
int reconnect = intent.getIntExtra(EXTRA_RECONNECT, 1);
int timeout = intent.getIntExtra(EXTRA_TIMEOUT, 2);
int timeout = intent.getIntExtra(EXTRA_TIMEOUT, 5);
int probeDelay = intent.getIntExtra(EXTRA_PROBE_DELAY, 1000);
int probeThreads = intent.getIntExtra(EXTRA_PROBE_THREADS, 1);
if (server == null || server.trim().isEmpty()) {
failStart("Server is required");
@@ -126,19 +130,22 @@ public class DragonService extends VpnService {
}
server = server.trim();
if (token == null) token = "";
if (wire == null || !(wire.equals("b") || wire.equals("x"))) wire = "auto";
if (wire == null || !(wire.equals("b") || wire.equals("bp") || wire.equals("x"))) wire = "auto";
chunkMax = Math.max(32, Math.min(1024 * 1024, chunkMax));
chunkMin = Math.max(32, Math.min(chunkMax, chunkMin));
batchMax = Math.max(1, Math.min(BATCH_LIMIT, batchMax));
batchMin = Math.max(1, Math.min(batchMax, batchMin));
reconnect = Math.max(0, reconnect);
timeout = Math.max(1, timeout);
probeDelay = Math.max(200, Math.min(30000, probeDelay));
probeThreads = Math.max(1, Math.min(16, probeThreads));
try {
AppLog.append("Starting DragonTCP → " + server + ":" + port);
AppLog.append(describeBatch(batchMin, batchMax));
AppLog.append(describeWire(wire));
Process process = startDragonCore(server, port, token, chunkMax, chunkMin, batchMax, batchMin, wire, reconnect, timeout);
AppLog.append("Wire probe: http://ip.dr2.site/ • delay " + probeDelay + " ms • threads " + probeThreads);
Process process = startDragonCore(server, port, token, chunkMax, chunkMin, batchMax, batchMin, wire, reconnect, timeout, probeDelay, probeThreads);
synchronized (stateLock) { coreProcess = process; }
startCoreLogReader(process);
@@ -206,7 +213,9 @@ public class DragonService extends VpnService {
int batchMin,
String wire,
int reconnect,
int timeout
int timeout,
int probeDelay,
int probeThreads
) throws Exception {
File executable = new File(getApplicationInfo().nativeLibraryDir, "libdragontcp_client.so");
if (!executable.exists()) throw new IllegalStateException("Embedded DragonTCP core is missing");
@@ -224,6 +233,8 @@ public class DragonService extends VpnService {
cmd.add("--chunk-max"); cmd.add(Integer.toString(chunkMax));
cmd.add("--chunk-pollers"); cmd.add("1");
cmd.add("--wire"); cmd.add(wire);
cmd.add("--wire-probe-delay"); cmd.add(probeDelay + "ms");
cmd.add("--wire-probe-threads"); cmd.add(Integer.toString(probeThreads));
cmd.add("--chunk-concurrency"); cmd.add(Integer.toString(batchMax));
cmd.add("--chunk-concurrency-min"); cmd.add(Integer.toString(batchMin));
cmd.add("--chunk-reconnect-every"); cmd.add(Integer.toString(reconnect));
@@ -249,9 +260,10 @@ public class DragonService extends VpnService {
/** Human-readable summary of the wire selection, for the log screen. */
private static String describeWire(String wire) {
if ("b".equals(wire)) return "Wire: B (manual)";
if ("x".equals(wire)) return "Wire: X (manual)";
return "Wire: auto (probing)";
if ("b".equals(wire)) return "Wire: B (discovering header profile)";
if ("bp".equals(wire)) return "Wire: BP";
if ("x".equals(wire)) return "Wire: X (discovering header profile)";
return "Wire: auto (discovering B/BP/X profile)";
}
private void startCoreLogReader(Process process) {
@@ -55,8 +55,11 @@ public class MainActivity extends Activity {
private EditText batchMax;
private EditText batchMin;
private EditText reconnect;
private EditText probeDelay;
private EditText probeThreads;
private Button wireAuto;
private Button wireB;
private Button wireBP;
private Button wireX;
private String wireMode = "auto";
private EditText timeout;
@@ -225,9 +228,11 @@ public class MainActivity extends Activity {
LinearLayout wireRow = row();
wireAuto = segmentButton("A");
wireB = segmentButton("B");
wireBP = segmentButton("BP");
wireX = segmentButton("X");
addSegment(wireRow, wireAuto);
addSegment(wireRow, wireB);
addSegment(wireRow, wireBP);
addSegment(wireRow, wireX);
wireCard.addView(wireRow);
wireHint = text("", 11, MUTED, true);
@@ -238,6 +243,7 @@ public class MainActivity extends Activity {
wireAuto.setOnClickListener(v -> setWireMode("auto"));
wireB.setOnClickListener(v -> setWireMode("b"));
wireBP.setOnClickListener(v -> setWireMode("bp"));
wireX.setOnClickListener(v -> setWireMode("x"));
// --------------------------------------------------------- record size
@@ -282,11 +288,17 @@ public class MainActivity extends Activity {
LinearLayout advancedCard = card("ADVANCED");
LinearLayout timing = row();
reconnect = addFieldToRow(timing, "Reconnect every", "1 = auto", "1", true, false, 0.58f);
timeout = addFieldToRow(timing, "Timeout (s)", "2", "2", true, false, 0.42f);
timeout = addFieldToRow(timing, "Timeout (s)", "5", "5", true, false, 0.42f);
advancedCard.addView(timing);
LinearLayout probing = row();
probeDelay = addFieldToRow(probing, "Probe delay (ms)", "1000", "1000", true, false, 0.65f);
probeThreads = addFieldToRow(probing, "Probe threads", "1", "1", true, false, 0.35f);
advancedCard.addView(probing);
advancedCard.addView(hint(
"1 = auto (recommended: probes the path, falls back to one request per "
+ "connection) • 0 = persistent • N = rotate every N requests"));
"1 = auto (recommended: starts persistent, then learns one request per "
+ "connection only if reuse really fails) • 0 = persistent • N = rotate every N requests\n"
+ "Probe delay spaces startup profile connections; 1000 ms is recommended. "
+ "Probe threads defaults to 1; more may trigger carrier limits."));
settings.addView(advancedCard, cardParams());
// ------------------------------------------------------------- buttons
@@ -469,17 +481,21 @@ public class MainActivity extends Activity {
wireMode = mode;
paintSegment(wireAuto, "auto".equals(mode));
paintSegment(wireB, "b".equals(mode));
paintSegment(wireBP, "bp".equals(mode));
paintSegment(wireX, "x".equals(mode));
if (wireHint == null) return;
if ("auto".equals(mode)) {
wireHint.setTextColor(ACCENT);
wireHint.setText("Auto: tries B, then X, keeping the one that connects.");
wireHint.setText("Auto: discovers a working B/BP/X profile and keeps it until restart.");
} else if ("b".equals(mode)) {
wireHint.setTextColor(OK);
wireHint.setText("Manual: B.");
wireHint.setText("B only; header profile is discovered automatically.");
} else if ("bp".equals(mode)) {
wireHint.setTextColor(OK);
wireHint.setText("BP only.");
} else {
wireHint.setTextColor(OK);
wireHint.setText("Manual: X.");
wireHint.setText("X only; header profile is discovered automatically.");
}
}
@@ -578,7 +594,9 @@ public class MainActivity extends Activity {
i.putExtra(DragonService.EXTRA_BATCH_MAX, p.getInt("batchMax", 1));
i.putExtra(DragonService.EXTRA_BATCH_MIN, p.getInt("batchMin", 1));
i.putExtra(DragonService.EXTRA_RECONNECT, p.getInt("reconnect", 1));
i.putExtra(DragonService.EXTRA_TIMEOUT, p.getInt("timeout", 2));
i.putExtra(DragonService.EXTRA_TIMEOUT, p.getInt("timeout", 5));
i.putExtra(DragonService.EXTRA_PROBE_DELAY, p.getInt("probeDelay", 1000));
i.putExtra(DragonService.EXTRA_PROBE_THREADS, p.getInt("probeThreads", 1));
if (Build.VERSION.SDK_INT >= 26) startForegroundService(i); else startService(i);
}
@@ -593,6 +611,8 @@ public class MainActivity extends Activity {
if (bMin > bMax) throw new IllegalArgumentException("Batch min must not exceed batch max");
int rec = parse(reconnect, 0, 1000000, "Reconnect every");
int tout = parse(timeout, 1, 120, "Timeout");
int delay = parse(probeDelay, 200, 30000, "Probe delay");
int threads = parse(probeThreads, 1, 16, "Probe threads");
getSharedPreferences(PREFS, MODE_PRIVATE).edit()
.putString("server", h)
@@ -605,6 +625,8 @@ public class MainActivity extends Activity {
.putInt("batchMin", bMin)
.putInt("reconnect", rec)
.putInt("timeout", tout)
.putInt("probeDelay", delay)
.putInt("probeThreads", threads)
.apply();
}
@@ -627,7 +649,9 @@ public class MainActivity extends Activity {
batchMax.setText(Integer.toString(p.getInt("batchMax", 1)));
batchMin.setText(Integer.toString(p.getInt("batchMin", 1)));
reconnect.setText(Integer.toString(p.getInt("reconnect", 1)));
timeout.setText(Integer.toString(p.getInt("timeout", 2)));
timeout.setText(Integer.toString(p.getInt("timeout", 5)));
probeDelay.setText(Integer.toString(p.getInt("probeDelay", 1000)));
probeThreads.setText(Integer.toString(p.getInt("probeThreads", 1)));
}
private void updateConnectionUi(boolean active, String rawStatus) {