Mult Protocol
This commit is contained in:
@@ -11,6 +11,8 @@ public final class AppLog {
|
||||
private static final int MAX_LINES = 600;
|
||||
private static final ArrayDeque<String> lines = new ArrayDeque<>();
|
||||
private static final CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>();
|
||||
private static String lastLine;
|
||||
private static int repeatCount;
|
||||
|
||||
private AppLog() {}
|
||||
|
||||
@@ -18,10 +20,36 @@ public final class AppLog {
|
||||
if (line == null) return;
|
||||
line = line.trim();
|
||||
if (line.isEmpty()) return;
|
||||
|
||||
// The core emits one adaptation line per proxied flow, so a busy page
|
||||
// load produces dozens of identical entries. Collapse consecutive
|
||||
// repeats into a single counted line instead of burying the log.
|
||||
String emit;
|
||||
synchronized (lines) {
|
||||
while (lines.size() >= MAX_LINES) lines.removeFirst();
|
||||
lines.addLast(line);
|
||||
if (line.equals(lastLine)) {
|
||||
repeatCount++;
|
||||
return;
|
||||
}
|
||||
if (repeatCount > 0) {
|
||||
String summary = " (previous line repeated " + repeatCount + " more times)";
|
||||
repeatCount = 0;
|
||||
push(summary);
|
||||
notifyListeners(summary);
|
||||
}
|
||||
lastLine = line;
|
||||
push(line);
|
||||
emit = line;
|
||||
}
|
||||
notifyListeners(emit);
|
||||
}
|
||||
|
||||
/** Caller must hold the {@code lines} lock. */
|
||||
private static void push(String line) {
|
||||
while (lines.size() >= MAX_LINES) lines.removeFirst();
|
||||
lines.addLast(line);
|
||||
}
|
||||
|
||||
private static void notifyListeners(String line) {
|
||||
for (Listener listener : listeners) {
|
||||
try { listener.onLine(line); } catch (Throwable ignored) {}
|
||||
}
|
||||
@@ -36,7 +64,11 @@ public final class AppLog {
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
synchronized (lines) { lines.clear(); }
|
||||
synchronized (lines) {
|
||||
lines.clear();
|
||||
lastLine = null;
|
||||
repeatCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void addListener(Listener listener) { listeners.addIfAbsent(listener); }
|
||||
|
||||
@@ -38,6 +38,8 @@ 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". */
|
||||
public static final String EXTRA_WIRE = "wire";
|
||||
public static final String EXTRA_CHUNK_MAX = "chunkMax";
|
||||
public static final String EXTRA_CHUNK_MIN = "chunkMin";
|
||||
/** Maximum download records per request. A transport setting, not a thread count. */
|
||||
@@ -110,11 +112,12 @@ public class DragonService extends VpnService {
|
||||
String server = intent.getStringExtra(EXTRA_SERVER);
|
||||
int port = intent.getIntExtra(EXTRA_PORT, 53);
|
||||
String token = intent.getStringExtra(EXTRA_TOKEN);
|
||||
String wire = intent.getStringExtra(EXTRA_WIRE);
|
||||
int chunkMax = intent.getIntExtra(EXTRA_CHUNK_MAX, 1024 * 1024);
|
||||
int chunkMin = intent.getIntExtra(EXTRA_CHUNK_MIN, 32);
|
||||
int batchMax = intent.getIntExtra(EXTRA_BATCH_MAX, 1);
|
||||
int batchMin = intent.getIntExtra(EXTRA_BATCH_MIN, 1);
|
||||
int reconnect = intent.getIntExtra(EXTRA_RECONNECT, 0);
|
||||
int reconnect = intent.getIntExtra(EXTRA_RECONNECT, 1);
|
||||
int timeout = intent.getIntExtra(EXTRA_TIMEOUT, 2);
|
||||
|
||||
if (server == null || server.trim().isEmpty()) {
|
||||
@@ -123,6 +126,7 @@ public class DragonService extends VpnService {
|
||||
}
|
||||
server = server.trim();
|
||||
if (token == null) token = "";
|
||||
if (wire == null || !(wire.equals("b") || 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));
|
||||
@@ -133,7 +137,8 @@ public class DragonService extends VpnService {
|
||||
try {
|
||||
AppLog.append("Starting DragonTCP → " + server + ":" + port);
|
||||
AppLog.append(describeBatch(batchMin, batchMax));
|
||||
Process process = startDragonCore(server, port, token, chunkMax, chunkMin, batchMax, batchMin, reconnect, timeout);
|
||||
AppLog.append(describeWire(wire));
|
||||
Process process = startDragonCore(server, port, token, chunkMax, chunkMin, batchMax, batchMin, wire, reconnect, timeout);
|
||||
synchronized (stateLock) { coreProcess = process; }
|
||||
|
||||
startCoreLogReader(process);
|
||||
@@ -199,6 +204,7 @@ public class DragonService extends VpnService {
|
||||
int chunkMin,
|
||||
int batchMax,
|
||||
int batchMin,
|
||||
String wire,
|
||||
int reconnect,
|
||||
int timeout
|
||||
) throws Exception {
|
||||
@@ -217,6 +223,7 @@ public class DragonService extends VpnService {
|
||||
cmd.add("--chunk-min"); cmd.add(Integer.toString(chunkMin));
|
||||
cmd.add("--chunk-max"); cmd.add(Integer.toString(chunkMax));
|
||||
cmd.add("--chunk-pollers"); cmd.add("1");
|
||||
cmd.add("--wire"); cmd.add(wire);
|
||||
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));
|
||||
@@ -239,6 +246,14 @@ public class DragonService extends VpnService {
|
||||
return "Download batch: adaptive " + min + "-" + max + " records per request";
|
||||
}
|
||||
|
||||
|
||||
/** 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)";
|
||||
}
|
||||
|
||||
private void startCoreLogReader(Process process) {
|
||||
Thread reader = new Thread(() -> {
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||
@@ -246,7 +261,7 @@ public class DragonService extends VpnService {
|
||||
while ((line = br.readLine()) != null) {
|
||||
// Keep the UI useful: adaptation changes and real errors only.
|
||||
String lower = line.toLowerCase();
|
||||
if (line.startsWith("adaptive ") || line.startsWith("path probe:") || lower.contains("error") || lower.contains("failed")) {
|
||||
if (line.startsWith("adaptive ") || line.startsWith("path probe:") || line.startsWith("wire") || lower.contains("error") || lower.contains("failed")) {
|
||||
AppLog.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,9 +53,14 @@ public class MainActivity extends Activity {
|
||||
private EditText batchMax;
|
||||
private EditText batchMin;
|
||||
private EditText reconnect;
|
||||
private Button wireAuto;
|
||||
private Button wireB;
|
||||
private Button wireX;
|
||||
private String wireMode = "auto";
|
||||
private EditText timeout;
|
||||
|
||||
private TextView batchHint;
|
||||
private TextView wireHint;
|
||||
private Button connectButton;
|
||||
private Button stopButton;
|
||||
private Button logsButton;
|
||||
@@ -159,6 +164,27 @@ public class MainActivity extends Activity {
|
||||
connectionCard.addView(connectionRow);
|
||||
settings.addView(connectionCard, cardParams());
|
||||
|
||||
// ---------------------------------------------------------------- wire
|
||||
LinearLayout wireCard = card("WIRE");
|
||||
wireCard.addView(hint("Networks differ in which mode they pass."));
|
||||
LinearLayout wireRow = row();
|
||||
wireAuto = segmentButton("A");
|
||||
wireB = segmentButton("B");
|
||||
wireX = segmentButton("X");
|
||||
addSegment(wireRow, wireAuto);
|
||||
addSegment(wireRow, wireB);
|
||||
addSegment(wireRow, wireX);
|
||||
wireCard.addView(wireRow);
|
||||
wireHint = text("", 11, MUTED, true);
|
||||
wireHint.setLineSpacing(0, 1.08f);
|
||||
wireHint.setPadding(dp(2), dp(8), dp(2), dp(2));
|
||||
wireCard.addView(wireHint);
|
||||
settings.addView(wireCard, cardParams());
|
||||
|
||||
wireAuto.setOnClickListener(v -> setWireMode("auto"));
|
||||
wireB.setOnClickListener(v -> setWireMode("b"));
|
||||
wireX.setOnClickListener(v -> setWireMode("x"));
|
||||
|
||||
// --------------------------------------------------------- record size
|
||||
LinearLayout sizeCard = card("RECORD SIZE (BYTES)");
|
||||
LinearLayout chunks = row();
|
||||
@@ -200,10 +226,12 @@ public class MainActivity extends Activity {
|
||||
// ------------------------------------------------------------ advanced
|
||||
LinearLayout advancedCard = card("ADVANCED");
|
||||
LinearLayout timing = row();
|
||||
reconnect = addFieldToRow(timing, "Reconnect every", "0 = persistent", "0", true, false, 0.58f);
|
||||
reconnect = addFieldToRow(timing, "Reconnect every", "1 = auto", "1", true, false, 0.58f);
|
||||
timeout = addFieldToRow(timing, "Timeout (s)", "2", "2", true, false, 0.42f);
|
||||
advancedCard.addView(timing);
|
||||
advancedCard.addView(hint("0 reconnect = persistent • 1 = auto • N = rotate every N requests"));
|
||||
advancedCard.addView(hint(
|
||||
"1 = auto (recommended: probes the path, falls back to one request per "
|
||||
+ "connection) • 0 = persistent • N = rotate every N requests"));
|
||||
settings.addView(advancedCard, cardParams());
|
||||
|
||||
// ------------------------------------------------------------- buttons
|
||||
@@ -284,6 +312,7 @@ public class MainActivity extends Activity {
|
||||
+ " on errors, recovers to " + max + ".");
|
||||
}
|
||||
|
||||
|
||||
private Integer readInt(EditText field) {
|
||||
if (field == null) return null;
|
||||
String raw = field.getText().toString().trim();
|
||||
@@ -364,6 +393,47 @@ public class MainActivity extends Activity {
|
||||
return edit;
|
||||
}
|
||||
|
||||
private Button segmentButton(String label) {
|
||||
Button b = new Button(this);
|
||||
b.setText(label);
|
||||
b.setTextSize(15);
|
||||
b.setTypeface(Typeface.DEFAULT, Typeface.BOLD);
|
||||
b.setAllCaps(false);
|
||||
return b;
|
||||
}
|
||||
|
||||
private void addSegment(LinearLayout parent, Button b) {
|
||||
LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(0, dp(46), 1f);
|
||||
if (parent.getChildCount() > 0) p.setMarginStart(dp(8));
|
||||
parent.addView(b, p);
|
||||
}
|
||||
|
||||
/** Selects the wire format and repaints the segmented control. */
|
||||
private void setWireMode(String mode) {
|
||||
wireMode = mode;
|
||||
paintSegment(wireAuto, "auto".equals(mode));
|
||||
paintSegment(wireB, "b".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.");
|
||||
} else if ("b".equals(mode)) {
|
||||
wireHint.setTextColor(OK);
|
||||
wireHint.setText("Manual: B.");
|
||||
} else {
|
||||
wireHint.setTextColor(OK);
|
||||
wireHint.setText("Manual: X.");
|
||||
}
|
||||
}
|
||||
|
||||
private void paintSegment(Button b, boolean selected) {
|
||||
if (b == null) return;
|
||||
b.setTextColor(selected ? Color.WHITE : Color.rgb(132, 142, 155));
|
||||
b.setBackground(roundRect(selected ? ACCENT : Color.rgb(32, 38, 47), 12,
|
||||
selected ? ACCENT : BORDER, 1));
|
||||
}
|
||||
|
||||
private LinearLayout row() {
|
||||
LinearLayout row = new LinearLayout(this);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
@@ -446,11 +516,12 @@ public class MainActivity extends Activity {
|
||||
i.putExtra(DragonService.EXTRA_SERVER, p.getString("server", ""));
|
||||
i.putExtra(DragonService.EXTRA_PORT, p.getInt("port", 53));
|
||||
i.putExtra(DragonService.EXTRA_TOKEN, p.getString("token", ""));
|
||||
i.putExtra(DragonService.EXTRA_WIRE, p.getString("wire", "auto"));
|
||||
i.putExtra(DragonService.EXTRA_CHUNK_MAX, p.getInt("max", 1048576));
|
||||
i.putExtra(DragonService.EXTRA_CHUNK_MIN, p.getInt("min", 32));
|
||||
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", 0));
|
||||
i.putExtra(DragonService.EXTRA_RECONNECT, p.getInt("reconnect", 1));
|
||||
i.putExtra(DragonService.EXTRA_TIMEOUT, p.getInt("timeout", 2));
|
||||
if (Build.VERSION.SDK_INT >= 26) startForegroundService(i); else startService(i);
|
||||
}
|
||||
@@ -471,6 +542,7 @@ public class MainActivity extends Activity {
|
||||
.putString("server", h)
|
||||
.putInt("port", p)
|
||||
.putString("token", token.getText().toString())
|
||||
.putString("wire", wireMode)
|
||||
.putInt("max", max)
|
||||
.putInt("min", min)
|
||||
.putInt("batchMax", bMax)
|
||||
@@ -493,11 +565,12 @@ public class MainActivity extends Activity {
|
||||
server.setText(p.getString("server", ""));
|
||||
port.setText(Integer.toString(p.getInt("port", 53)));
|
||||
token.setText(p.getString("token", ""));
|
||||
setWireMode(p.getString("wire", "auto"));
|
||||
chunkMax.setText(Integer.toString(p.getInt("max", 1048576)));
|
||||
chunkMin.setText(Integer.toString(p.getInt("min", 32)));
|
||||
batchMax.setText(Integer.toString(p.getInt("batchMax", 1)));
|
||||
batchMin.setText(Integer.toString(p.getInt("batchMin", 1)));
|
||||
reconnect.setText(Integer.toString(p.getInt("reconnect", 0)));
|
||||
reconnect.setText(Integer.toString(p.getInt("reconnect", 1)));
|
||||
timeout.setText(Integer.toString(p.getInt("timeout", 2)));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user