Skip to content

Commit 8d992c1

Browse files
[controller] actually add the network profiler
1 parent b5d16c8 commit 8d992c1

1 file changed

Lines changed: 146 additions & 0 deletions

File tree

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
============================================================================
3+
Name : Aria2NetworkProfiler.java
4+
Contributors: AppDevForAll
5+
Copyright (c) 2026 AppDevForAll
6+
Description : Network profiler to determine optimal network routing (IPv4 vs IPv6).
7+
============================================================================
8+
*/
9+
10+
package org.iiab.controller;
11+
12+
import android.os.Handler;
13+
import android.util.Log;
14+
15+
import java.io.BufferedReader;
16+
import java.io.File;
17+
import java.io.InputStreamReader;
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
import java.util.regex.Matcher;
21+
import java.util.regex.Pattern;
22+
23+
public class Aria2NetworkProfiler {
24+
25+
private static final String TAG = "IIAB-NetworkProfiler";
26+
private static final int TEST_DURATION_MS = 6000; // 6 seconds to bypass initial QoS burst
27+
28+
/**
29+
* Runs a real-world speed test by downloading the target file for a few seconds.
30+
* Returns TRUE if IPv4 proves to be more stable and faster.
31+
*/
32+
public static boolean shouldForceIpv4(File aria2Bin, File downloadDir, File dhtFile, String targetUrl, Handler mainHandler, Aria2Manager.DownloadListener listener) {
33+
Log.i(TAG, "Starting Network Profiling (Time-Boxed Race)...");
34+
35+
// 1. Test IPv4 (Forced)
36+
mainHandler.post(() -> listener.onProgress(0, "Test IPv4", "Wait"));
37+
Log.i(TAG, "=== TEST 1: FORCED IPv4 ROUTE ===");
38+
double speedIpv4 = runTimeBoxedTest(aria2Bin, downloadDir, dhtFile, targetUrl, true);
39+
40+
// 2. Test Default (Usually IPv6 preferred)
41+
mainHandler.post(() -> listener.onProgress(0, "Test IPv6", "Wait"));
42+
Log.i(TAG, "=== TEST 2: DEFAULT ROUTE (IPv6 Preferred) ===");
43+
double speedDefault = runTimeBoxedTest(aria2Bin, downloadDir, dhtFile, targetUrl, false);
44+
45+
Log.i(TAG, "=== FINAL PROFILING RESULTS ===");
46+
Log.i(TAG, String.format("Sustained Speed IPv4: %.2f KB/s", speedIpv4));
47+
Log.i(TAG, String.format("Sustained Speed Default: %.2f KB/s", speedDefault));
48+
49+
boolean forceIpv4 = false;
50+
51+
// If Default/IPv6 is dead (0) or IPv4 is at least 20% faster, force IPv4.
52+
if (speedDefault == 0 || speedIpv4 > (speedDefault * 1.2)) {
53+
Log.w(TAG, "Conclusion: QoS or routing is throttling the default route. Forcing IPv4.");
54+
forceIpv4 = true;
55+
} else {
56+
Log.i(TAG, "Conclusion: Default route is stable. Using default configuration.");
57+
}
58+
59+
// Show the winner in the UI
60+
final String winnerText = forceIpv4 ? "✓ IPv4" : "✓ IPv6";
61+
mainHandler.post(() -> listener.onProgress(0, winnerText, "Wait"));
62+
63+
// Brief pause so the user can actually read the result before the real download overwrites it
64+
try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
65+
66+
return forceIpv4;
67+
}
68+
69+
private static double runTimeBoxedTest(File aria2Bin, File downloadDir, File dhtFile, String url, boolean forceIpv4) {
70+
Process process = null;
71+
double lastRecordedSpeedKB = 0.0;
72+
73+
try {
74+
List<String> command = new ArrayList<>();
75+
command.add(aria2Bin.getAbsolutePath());
76+
command.add("--dir=" + downloadDir.getAbsolutePath());
77+
command.add("--continue=true"); // CRITICAL: Do not lose downloaded data
78+
command.add("--file-allocation=none");
79+
80+
// Match your baseline configuration
81+
command.add("--max-connection-per-server=4");
82+
command.add("--split=4");
83+
command.add("--enable-dht=true");
84+
command.add("--dht-file-path=" + dhtFile.getAbsolutePath());
85+
command.add("--check-certificate=false");
86+
87+
if (forceIpv4) {
88+
command.add("--disable-ipv6=true");
89+
}
90+
91+
command.add("--console-log-level=warn");
92+
command.add("--summary-interval=1"); // Need speed output every second
93+
command.add("--download-result=hide");
94+
command.add(url);
95+
96+
ProcessBuilder pb = new ProcessBuilder(command);
97+
pb.redirectErrorStream(true);
98+
process = pb.start();
99+
100+
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
101+
String line;
102+
103+
// Regex to capture speed (e.g., DL:1.5MiB or DL:750KiB)
104+
Pattern pattern = Pattern.compile("DL:([0-9.]+)([K|M|G]?iB)");
105+
106+
long startTime = System.currentTimeMillis();
107+
108+
// Read output until time runs out
109+
while ((System.currentTimeMillis() - startTime) < TEST_DURATION_MS) {
110+
if (reader.ready()) {
111+
line = reader.readLine();
112+
if (line == null) break;
113+
114+
Log.d(TAG, "[Aria2-Test] " + line);
115+
116+
Matcher matcher = pattern.matcher(line);
117+
if (matcher.find()) {
118+
double value = Double.parseDouble(matcher.group(1));
119+
String unit = matcher.group(2);
120+
121+
// Standardize to Kilobytes per second (KB/s)
122+
if (unit.equals("MiB")) {
123+
lastRecordedSpeedKB = value * 1024;
124+
} else if (unit.equals("GiB")) {
125+
lastRecordedSpeedKB = value * 1024 * 1024;
126+
} else {
127+
lastRecordedSpeedKB = value; // KiB
128+
}
129+
}
130+
} else {
131+
Thread.sleep(50); // Short pause to prevent CPU burn
132+
}
133+
}
134+
135+
} catch (Exception e) {
136+
Log.e(TAG, "Speed test error", e);
137+
} finally {
138+
if (process != null) {
139+
process.destroy(); // Kill process mercilessly after 6 seconds
140+
try { process.waitFor(); } catch (InterruptedException ignored) {}
141+
}
142+
}
143+
144+
return lastRecordedSpeedKB;
145+
}
146+
}

0 commit comments

Comments
 (0)