Skip to content

Commit 8b6ef02

Browse files
committed
feat: upstream sync v2026.04.11.044523-0639f8b + Android fixes
Engine (Go): - arq.go: remove dataNackMu, fix mutex handling - balancer.go: add modes 5-8 (WeightedRR, IPHash, LeastLoad, Adaptive) - client.go, client_utils.go: upstream sync - mtu.go: upstream sync - session.go: upstream sync - config/client.go, config/server.go: upstream sync - udpserver: server.go, server_postsession.go, server_runtime.go, server_session.go, session.go synced; add reuseport_unix/fallback - vpnproto/session_accept.go: new file from upstream - internal/config/json_config.go: new file from upstream Android: - ProfileEntity.kt: updated defaults for new balancer modes - ProfileEditScreen.kt: add modes 5-8 to UI; MTU folder fallback detection with orange warning card (MtuDirWarningCard) - HomeScreen.kt: upstream sync UI updates - DnsTunnelVpnService.kt: fix notification lingering after disconnect (race condition between speed monitor and stopForeground) - AndroidManifest.xml: upstream sync - build.gradle.kts: bump to v1.0.9 local; set upstream v2026.04.11.044523-0639f8b Docs: - README_FA.md: add wallet support addresses section
1 parent 573e0ab commit 8b6ef02

25 files changed

Lines changed: 1993 additions & 329 deletions

README_FA.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,19 @@ adb install "app\build\outputs\apk\debug\MasterDnsVPN-1.0.0-beta-arm64-v8a.apk"
154154

155155
---
156156

157+
## حمایت از پروژه
158+
159+
اگر این پروژه برایتان مفید بوده، می‌توانید از توسعه آن حمایت کنید:
160+
161+
| شبکه | آدرس |
162+
|------|------|
163+
| TON | `UQBW_LoEhcYPIzZL_dzp-OMsqI5uAwv8p6dXy8wzzkPU-CQQ` |
164+
| BNB / USDT (BEP-20) | `0x951acaf8d4b61a000d3b5c697abcabf52973d0cf` |
165+
| TRX | `TL4Kej6DjJmT9gQ5ghmQcvsEUHPdnNNPyj` |
166+
| SOL | `45kAfGyh13bcyYTdbNLkVfBGtMgq4WMijLgdBK9G9ugN` |
167+
168+
---
169+
157170
## لایسنس
158171

159172
فایل [LICENSE](LICENSE) را ببینید.

android/app/build.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ android {
1818
targetSdk = 35
1919
// Read from Gradle properties injected by CI (-PversionCode=X -PversionName=Y)
2020
// Fallback to hardcoded values for local development.
21-
versionCode = (project.findProperty("versionCode") as? String)?.toIntOrNull() ?: 7
22-
versionName = project.findProperty("versionName") as? String ?: "1.0.7"
21+
versionCode = (project.findProperty("versionCode") as? String)?.toIntOrNull() ?: 9
22+
versionName = project.findProperty("versionName") as? String ?: "1.0.9"
2323

2424
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2525

2626
buildConfigField(
2727
"String",
2828
"UPSTREAM_VERSION",
29-
"\"${project.findProperty("upstreamVersion") as? String ?: "v2026.04.09.020149-a788a98"}\""
29+
"\"${project.findProperty("upstreamVersion") as? String ?: "v2026.04.11.044523-0639f8b"}\""
3030
)
3131

3232
// Include all ABIs in universal APK

android/app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
<!-- Vibration feedback (e.g. when VPN session becomes ready) -->
3232
<uses-permission android:name="android.permission.VIBRATE" />
3333

34+
<!-- Hotspot sharing: detect AP interface on Android 13+ -->
35+
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
36+
android:usesPermissionFlags="neverForLocation" />
37+
3438
<!-- Query all installed packages for per-app VPN selection (Android 11+) -->
3539
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
3640
tools:ignore="QueryAllPackagesPermission" />

android/app/src/main/java/com/masterdnsvpn/profile/ProfileEntity.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ data class ProfileEntity(
5454
val localDnsCacheFlushSec: Double = 60.0,
5555

5656
// Section 4: Balancing & Duplication
57-
val resolverBalancingStrategy: Int = 0,
57+
val resolverBalancingStrategy: Int = 2,
5858
val packetDuplicationCount: Int = 2,
5959
val setupPacketDuplicationCount: Int = 2,
6060
val streamResolverFailoverResendThreshold: Int = 2,
@@ -121,10 +121,10 @@ data class ProfileEntity(
121121
val saveMtuServersToFile: Boolean = false,
122122
val mtuServersFileDir: String = "", // empty = use internal profile dir; set to absolute path for accessible output
123123
val mtuServersFileName: String = "masterdnsvpn_success_test_{time}.log",
124-
val mtuServersFileFormat: String = "{IP} - UP: {UP_MTU} DOWN: {DOWN-MTU}",
124+
val mtuServersFileFormat: String = "{IP} ({DOMAIN}) - UP: {UP_MTU} DOWN: {DOWN-MTU}",
125125
val mtuUsingSeparatorText: String = "",
126-
val mtuRemovedServerLogFormat: String = "Resolver {IP} removed at {TIME} due to {CAUSE}",
127-
val mtuAddedServerLogFormat: String = "Resolver {IP} added back at {TIME} (UP {UP_MTU}, DOWN {DOWN_MTU})",
126+
val mtuRemovedServerLogFormat: String = "Resolver {IP} ({DOMAIN}) removed at {TIME} due to {CAUSE}",
127+
val mtuAddedServerLogFormat: String = "Resolver {IP} ({DOMAIN}) added back at {TIME} (UP {UP_MTU}, DOWN {DOWN_MTU})",
128128

129129
// Section 12: Logging
130130
val logLevel: String = "INFO",

android/app/src/main/java/com/masterdnsvpn/service/DnsTunnelVpnService.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ class DnsTunnelVpnService : VpnService() {
312312
private fun performStop() {
313313
intentionalStop = true
314314
scope.launch {
315+
getSystemService(NotificationManager::class.java)
316+
?.cancel(TunnelNotification.NOTIFICATION_ID)
315317
try { bridge.stopTunBridge() } catch (_: Exception) {}
316318
try { bridge.stopSocksBalancer() } catch (_: Exception) {}
317319

@@ -343,6 +345,8 @@ class DnsTunnelVpnService : VpnService() {
343345
vpnInterface = null
344346

345347
stopForeground(STOP_FOREGROUND_REMOVE)
348+
getSystemService(NotificationManager::class.java)
349+
?.cancel(TunnelNotification.NOTIFICATION_ID)
346350
stopSelf()
347351
}
348352
}
@@ -360,7 +364,7 @@ class DnsTunnelVpnService : VpnService() {
360364
val nm = getSystemService(NotificationManager::class.java)
361365
while (isActive) {
362366
delay(1_000)
363-
if (!isActive) break
367+
if (!isActive || intentionalStop) break
364368
val (tunUp, tunDown) = bridge.getBandwidth()
365369
val tunUpSpeed = (tunUp - prevTunUp).coerceAtLeast(0L)
366370
val tunDownSpeed = (tunDown - prevTunDown).coerceAtLeast(0L)
@@ -384,6 +388,9 @@ class DnsTunnelVpnService : VpnService() {
384388
// vpnInterface is already closed in performStop() — guard against double-close.
385389
try { connectivityManager.unregisterNetworkCallback(networkCallback) } catch (_: Exception) {}
386390
bridge.registerProtectCallback(null)
391+
stopForeground(STOP_FOREGROUND_REMOVE)
392+
getSystemService(NotificationManager::class.java)
393+
?.cancel(TunnelNotification.NOTIFICATION_ID)
387394
if (vpnInterface != null) {
388395
vpnInterface?.close()
389396
vpnInterface = null

android/app/src/main/java/com/masterdnsvpn/ui/screens/HomeScreen.kt

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,15 @@ fun HomeScreen(
8686
val coroutineScope = rememberCoroutineScope()
8787
var startErrorMessage by remember { mutableStateOf<String?>(null) }
8888

89+
// Nearby Wi-Fi Devices permission (Android 13+) — needed for hotspot AP interface detection
90+
val nearbyWifiPermissionLauncher = rememberLauncherForActivityResult(
91+
ActivityResultContracts.RequestPermission()
92+
) { granted ->
93+
// Proceed regardless — hotspot toggle will show an error if AP IP can't be detected
94+
if (!hotspotState.isRunning) hotspotVm.toggle(ctx)
95+
showHotspotDialog = true
96+
}
97+
8998
LaunchedEffect(Unit) {
9099
vm.startError.collect { msg ->
91100
startErrorMessage = msg
@@ -348,6 +357,10 @@ fun HomeScreen(
348357
onClick = {
349358
if (hotspotState.isRunning) {
350359
showHotspotDialog = true
360+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
361+
ctx.checkSelfPermission(android.Manifest.permission.NEARBY_WIFI_DEVICES) != android.content.pm.PackageManager.PERMISSION_GRANTED
362+
) {
363+
nearbyWifiPermissionLauncher.launch(android.Manifest.permission.NEARBY_WIFI_DEVICES)
351364
} else {
352365
hotspotVm.toggle(ctx)
353366
showHotspotDialog = true
@@ -416,12 +429,16 @@ fun HomeScreen(
416429

417430
items(uiState.profiles, key = { it.id }) { profile ->
418431
val monitorInfo = monitorState.activeProfiles.find { it.profile.id == profile.id }
432+
val totalBytes = monitorVm.bandwidthPrefs.getTotalBytes(profile.id)
433+
val wireTotalBytes = monitorVm.bandwidthPrefs.getWireTotalBytes(profile.id)
419434
ProfileCard(
420435
profile = profile,
421436
isRunning = uiState.runningProfileIds.contains(profile.id),
422437
isBusy = uiState.busyIds.contains(profile.id),
423438
monitorInfo = monitorInfo,
424439
hasError = startErrorMessage != null,
440+
totalUsageBytes = totalBytes,
441+
wireTotalBytes = wireTotalBytes,
425442
onConnect = {
426443
pendingVpnProfileId = profile.id
427444
vm.connectProfile(ctx, profile)
@@ -814,6 +831,8 @@ private fun ProfileCard(
814831
isBusy: Boolean,
815832
monitorInfo: com.masterdnsvpn.ui.viewmodel.ProfileMonitorInfo?,
816833
hasError: Boolean = false,
834+
totalUsageBytes: Long = 0L,
835+
wireTotalBytes: Long = 0L,
817836
onConnect: () -> Unit,
818837
onDisconnect: () -> Unit,
819838
onEdit: () -> Unit,
@@ -930,38 +949,40 @@ private fun ProfileCard(
930949
}
931950
}
932951

933-
// Compact usage badges — right side of header row
934-
if (monitorInfo != null) {
935-
val upBytes = monitorInfo.uploadBytes
936-
val downBytes = monitorInfo.downloadBytes
937-
if (upBytes > 0L || downBytes > 0L) {
938-
Spacer(Modifier.width(6.dp))
939-
Row(
940-
horizontalArrangement = Arrangement.spacedBy(4.dp),
941-
verticalAlignment = Alignment.CenterVertically,
942-
) {
952+
// Usage badges — actual traffic + MasterDNS overhead (side by side)
953+
if (totalUsageBytes > 0 || wireTotalBytes > 0) {
954+
Row(
955+
horizontalArrangement = Arrangement.spacedBy(4.dp),
956+
verticalAlignment = Alignment.CenterVertically,
957+
) {
958+
// Actual traffic (TUN — what apps really consumed)
959+
if (totalUsageBytes > 0) {
943960
Surface(
944961
color = CyanAccent.copy(alpha = 0.12f),
945-
shape = RoundedCornerShape(4.dp),
962+
shape = RoundedCornerShape(8.dp),
946963
) {
947964
Text(
948-
"${formatUsageBytes(downBytes)}",
965+
"${formatUsageBytes(totalUsageBytes)}",
966+
modifier = Modifier.padding(horizontal = 8.dp, vertical = 3.dp),
949967
color = CyanAccent,
950-
fontSize = 9.sp,
968+
fontSize = 10.sp,
951969
fontWeight = FontWeight.Medium,
952-
modifier = Modifier.padding(horizontal = 5.dp, vertical = 1.dp),
953970
)
954971
}
972+
}
973+
// MasterDNS overhead (wire - actual = extra from ARQ/duplication)
974+
val overhead = (wireTotalBytes - totalUsageBytes).coerceAtLeast(0L)
975+
if (overhead > 0) {
955976
Surface(
956-
color = AmberWarn.copy(alpha = 0.12f),
957-
shape = RoundedCornerShape(4.dp),
977+
color = Color(0xFFFFAB40).copy(alpha = 0.12f),
978+
shape = RoundedCornerShape(8.dp),
958979
) {
959980
Text(
960-
"${formatUsageBytes(upBytes)}",
961-
color = AmberWarn,
962-
fontSize = 9.sp,
981+
"+${formatUsageBytes(overhead)}",
982+
modifier = Modifier.padding(horizontal = 8.dp, vertical = 3.dp),
983+
color = Color(0xFFFFAB40),
984+
fontSize = 10.sp,
963985
fontWeight = FontWeight.Medium,
964-
modifier = Modifier.padding(horizontal = 5.dp, vertical = 1.dp),
965986
)
966987
}
967988
}

0 commit comments

Comments
 (0)