Skip to content

Commit 7388b09

Browse files
committed
Optimize low-bar network performance
1 parent b4b5d7e commit 7388b09

11 files changed

Lines changed: 1543 additions & 2 deletions

File tree

app/app/src/main/java/com/bringyour/network/MainService.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,9 @@ import kotlin.concurrent.thread
341341

342342
val builder = Builder()
343343
builder.setSession("URnetwork")
344-
builder.setMtu(1440)
344+
// Matches connect's provider packetizer and keeps one encrypted tunnel
345+
// packet within H3's single-DATAGRAM payload ceiling.
346+
builder.setMtu(Sdk.getDefaultTunnelMtu())
345347
builder.setBlocking(false)
346348
builder.setUnderlyingNetworks(null)
347349
val tunnelIncludedAppIds = configuration.includedAppIds
@@ -563,7 +565,7 @@ import kotlin.concurrent.thread
563565
}
564566
val builder = Builder()
565567
.setSession("URnetwork — sign in required")
566-
.setMtu(1440)
568+
.setMtu(Sdk.getDefaultTunnelMtu())
567569
.setBlocking(false)
568570
.setUnderlyingNetworks(null)
569571
.addDisallowedApplication(packageName)

app/app/src/main/java/com/bringyour/network/ui/MainNavHost.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ import com.bringyour.network.ui.connect.providerlocations.MockLocationGuideScree
128128
import com.bringyour.network.ui.connect.providerlocations.MockLocationSection
129129
import com.bringyour.network.ui.connect.providerlocations.ProviderLocationsScreen
130130
import com.bringyour.network.ui.stats.DnsSettingsScreen
131+
import com.bringyour.network.ui.stats.TransportSettingsKind
132+
import com.bringyour.network.ui.stats.TransportSettingsScreen
131133
import com.bringyour.network.ui.stats.SplitRulesScreen
132134
import com.bringyour.network.ui.theme.Pink
133135
import com.bringyour.network.ui.upgrade.UpgradeScreen
@@ -925,6 +927,19 @@ fun MainNavContent(
925927
)
926928
}
927929

930+
composable<Route.TransportSettings>(
931+
enterTransition = NavigationAnimations.enterTransition(),
932+
exitTransition = NavigationAnimations.exitTransition(),
933+
popEnterTransition = NavigationAnimations.popEnterTransition(),
934+
popExitTransition = NavigationAnimations.popExitTransition()
935+
) { backStackEntry ->
936+
val route: Route.TransportSettings = backStackEntry.toRoute()
937+
TransportSettingsScreen(
938+
navController = navController,
939+
kind = if (route.provider) TransportSettingsKind.PROVIDER else TransportSettingsKind.CLIENT,
940+
)
941+
}
942+
928943
composable<Route.ProviderLocations>(
929944
enterTransition = NavigationAnimations.enterTransition(),
930945
exitTransition = NavigationAnimations.exitTransition(),

app/app/src/main/java/com/bringyour/network/ui/MainNavViewModel.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ sealed class Route {
108108
@Serializable object SplitRules : Route()
109109
@Serializable object AppSplitRules : Route()
110110
@Serializable object DnsSettings : Route()
111+
@Serializable data class TransportSettings(val provider: Boolean) : Route()
111112

112113
@Serializable object ProviderLocations : Route()
113114

app/app/src/main/java/com/bringyour/network/ui/stats/ConnectStatsSections.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,20 @@ fun ConnectStatsSections(
9090

9191
Spacer(modifier = Modifier.height(12.dp))
9292

93+
/**
94+
* The remote traffic of the window by transport, full width under
95+
* the remote plot. Tap to open the transport settings. The child tap
96+
* wins over the card's tap.
97+
*/
98+
TransportDistributionBar(
99+
distribution = throughputViewModel.clientTransportDistribution,
100+
onClick = {
101+
navController.navigate(Route.TransportSettings(provider = false))
102+
}
103+
)
104+
105+
Spacer(modifier = Modifier.height(12.dp))
106+
93107
TransferChart(
94108
points = throughputViewModel.clientPoints,
95109
route = ThroughputRoute.BLOCK,

app/app/src/main/java/com/bringyour/network/ui/stats/ProviderStatsSection.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,21 @@ fun ProviderStatsSection(
7979

8080
Spacer(modifier = Modifier.height(12.dp))
8181

82+
/**
83+
* The relayed traffic of the window by the transport this
84+
* device used to carry it, under the provider plot. Tap to
85+
* open the provider transport settings. The child tap wins over
86+
* the section's tap.
87+
*/
88+
TransportDistributionBar(
89+
distribution = throughputViewModel.providerTransportDistribution,
90+
onClick = {
91+
navController.navigate(Route.TransportSettings(provider = true))
92+
}
93+
)
94+
95+
Spacer(modifier = Modifier.height(12.dp))
96+
8297
TransferChart(
8398
points = throughputViewModel.providerPoints,
8499
route = ThroughputRoute.BLOCK,

app/app/src/main/java/com/bringyour/network/ui/stats/ThroughputViewModel.kt

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import com.bringyour.sdk.ContractViewController
1515
import com.bringyour.sdk.DeviceLocal
1616
import com.bringyour.sdk.Sub
1717
import com.bringyour.sdk.ThroughputPointList
18+
import com.bringyour.sdk.TransportDistribution
1819
import dagger.hilt.android.lifecycle.HiltViewModel
1920
import kotlinx.coroutines.launch
2021
import javax.inject.Inject
@@ -58,6 +59,125 @@ enum class ThroughputRoute {
5859
}
5960
}
6061

62+
/**
63+
* One transport's slice of the window's remote traffic, ready to render as a
64+
* segment of the transport bar plus its legend entry. A mirror of the sdk's
65+
* `TransportShare`: every render value (share, cumulative boundary, whole
66+
* percent, used, enabled) is computed by the sdk view controller so the math
67+
* is shared and tested once for every platform.
68+
*/
69+
data class TransportShareUi(
70+
val transportType: TransportTypeUi,
71+
val egressBytes: Long = 0,
72+
val ingressBytes: Long = 0,
73+
val egressPackets: Long = 0,
74+
val ingressPackets: Long = 0,
75+
/**
76+
* fraction of the window's remote bytes, 0..1; 0 while idle
77+
*/
78+
val share: Double = 0.0,
79+
/**
80+
* the right edge of the segment as a fraction of the bar width: the
81+
* cumulative share through this transport in stable order. Rendering every
82+
* segment from its neighbours' boundaries tiles exactly 100% of the bar
83+
*/
84+
val boundary: Double = 0.0,
85+
/**
86+
* whole percent for the legend; the used percents sum to exactly 100.
87+
* A sliver can round to 0 while still used
88+
*/
89+
val percent: Int = 0,
90+
/**
91+
* carried traffic in the window: draws a segment and a legend entry
92+
*/
93+
val used: Boolean = false,
94+
/**
95+
* enabled by the transport settings; unused footer entry when idle
96+
*/
97+
val enabled: Boolean = false,
98+
) {
99+
val byteCount: Long
100+
get() = egressBytes + ingressBytes
101+
}
102+
103+
/**
104+
* The window's remote traffic partitioned by the transport that carried it,
105+
* in the sdk's stable order with every transport present. Follows the same
106+
* window as the throughput points, so it drains to inactive as traffic ages
107+
* out. A mirror of the sdk's `TransportDistribution`.
108+
*/
109+
data class TransportDistributionUi(
110+
/**
111+
* stable order: h3, h1, dns, dnspump, p2p, unknown
112+
*/
113+
val shares: List<TransportShareUi>,
114+
val byteCount: Long,
115+
/**
116+
* whether any transport carried traffic in the window
117+
*/
118+
val active: Boolean,
119+
) {
120+
/**
121+
* the segment boundaries in stable order, the vector the bar animates
122+
*/
123+
val boundaries: List<Float>
124+
get() = shares.map { it.boundary.toFloat() }
125+
126+
/**
127+
* the transports with traffic in the window, stable order
128+
*/
129+
val used: List<TransportShareUi>
130+
get() = shares.filter { it.used }
131+
132+
/**
133+
* the enabled transports without traffic in the window, stable order
134+
*/
135+
val unused: List<TransportShareUi>
136+
get() = shares.filter { it.enabled && !it.used }
137+
138+
companion object {
139+
val Empty = TransportDistributionUi(shares = listOf(), byteCount = 0, active = false)
140+
141+
/**
142+
* maps the sdk distribution, dropping transport types this app does
143+
* not know (a newer sdk vocabulary)
144+
*/
145+
fun fromSdk(distribution: TransportDistribution?): TransportDistributionUi {
146+
if (distribution == null) {
147+
return Empty
148+
}
149+
val shares = mutableListOf<TransportShareUi>()
150+
val list = distribution.shares
151+
if (list != null) {
152+
val n = list.len()
153+
for (i in 0 until n) {
154+
val share = list.get(i) ?: continue
155+
val transportType = TransportTypeUi.fromRawValue(share.transportType) ?: continue
156+
shares.add(
157+
TransportShareUi(
158+
transportType = transportType,
159+
egressBytes = share.egressByteCount,
160+
ingressBytes = share.ingressByteCount,
161+
egressPackets = share.egressPacketCount,
162+
ingressPackets = share.ingressPacketCount,
163+
share = share.share,
164+
boundary = share.boundary,
165+
percent = share.percent.toInt(),
166+
used = share.used,
167+
enabled = share.enabled,
168+
)
169+
)
170+
}
171+
}
172+
return TransportDistributionUi(
173+
shares = shares,
174+
byteCount = distribution.byteCount,
175+
active = distribution.active,
176+
)
177+
}
178+
}
179+
}
180+
61181
/**
62182
* Wraps the sdk contract view controller and publishes the live
63183
* client and provider throughput series
@@ -84,6 +204,16 @@ class ThroughputViewModel @Inject constructor(
84204
var providerPoints by mutableStateOf<List<ThroughputPointUi>>(listOf())
85205
private set
86206

207+
/**
208+
* the remote traffic of the window partitioned by transport, ready to
209+
* render (see `TransportDistributionUi`)
210+
*/
211+
var clientTransportDistribution by mutableStateOf(TransportDistributionUi.Empty)
212+
private set
213+
214+
var providerTransportDistribution by mutableStateOf(TransportDistributionUi.Empty)
215+
private set
216+
87217
/**
88218
* false when the device has no provider (providing disabled)
89219
*/
@@ -121,6 +251,8 @@ class ThroughputViewModel @Inject constructor(
121251
private fun setupDevice(device: DeviceLocal?) {
122252
clientPoints = listOf()
123253
providerPoints = listOf()
254+
clientTransportDistribution = TransportDistributionUi.Empty
255+
providerTransportDistribution = TransportDistributionUi.Empty
124256
hasProviderStats = false
125257
controllerOwner.setDevice(device)
126258
}
@@ -135,6 +267,20 @@ class ThroughputViewModel @Inject constructor(
135267
update()
136268
}
137269
})
270+
// the distribution's enabled flags follow the device transport settings
271+
// (the view controller caches them from these same listeners). Re-read
272+
// the distributions on a policy change so the unused footer is right
273+
// while the window is idle and no throughput tick is due.
274+
subs.add(device.addTransportSettingsChangeListener {
275+
viewModelScope.launch {
276+
updateTransportDistributions()
277+
}
278+
})
279+
subs.add(device.addProviderTransportSettingsChangeListener {
280+
viewModelScope.launch {
281+
updateTransportDistributions()
282+
}
283+
})
138284
vc.start()
139285
update()
140286
return vc
@@ -155,9 +301,28 @@ class ThroughputViewModel @Inject constructor(
155301
val vc = contractVc ?: return
156302
clientPoints = mapPoints(vc.throughputPoints)
157303
providerPoints = mapPoints(vc.providerThroughputPoints)
304+
updateTransportDistributions()
158305
hasProviderStats = vc.providerPacketStats != null
159306
}
160307

308+
/**
309+
* Publishes the window transport distributions from the same view
310+
* controller snapshot as the points. The distribution is inactive while
311+
* the window is idle; only a real change is published so an idle tick
312+
* doesn't retrigger the bar.
313+
*/
314+
private fun updateTransportDistributions() {
315+
val vc = contractVc ?: return
316+
val clientDistribution = TransportDistributionUi.fromSdk(vc.transportDistribution)
317+
if (clientDistribution != clientTransportDistribution) {
318+
clientTransportDistribution = clientDistribution
319+
}
320+
val providerDistribution = TransportDistributionUi.fromSdk(vc.providerTransportDistribution)
321+
if (providerDistribution != providerTransportDistribution) {
322+
providerTransportDistribution = providerDistribution
323+
}
324+
}
325+
161326
private fun mapPoints(list: ThroughputPointList?): List<ThroughputPointUi> {
162327
if (list == null) {
163328
return listOf()

0 commit comments

Comments
 (0)