app: an IMS network is not a blocked measurement
rmnet_data1 on the OnePlus is the carrier's IMS/VoLTE network: it carries IMS&MMTEL but neither INTERNET nor NOT_RESTRICTED, so binding needs CONNECTIVITY_USE_RESTRICTED_NETWORKS - signature-level, unobtainable. EPERM there is permanent and says nothing about any network's health, but the constraint detector counted it, so a phone with VoLTE reported 'measurement blocked' on every run and every verdict came out INCONCLUSIVE. Capabilities now decide: networks an app may never bind are recorded as app_usable:false in networks[] and skipped, rather than reported as something the run failed to do. Also retires the 'while it tears down' wording, which was a guess that turned out to be wrong - the refusal outlasts the VPN indefinitely, so the text now names the VPN as the usual cause without asserting a timeline it cannot know. App version 0.2.4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d420a71a01
commit
ab6e278272
@@ -40,6 +40,12 @@ object ConstraintDetector {
|
||||
for (e in entries) {
|
||||
// The tunnel itself stays bindable — it is the underlying networks the OS walls off.
|
||||
if (e.model.transport == Transport.VPN) continue
|
||||
// Networks an app may never bind (carrier IMS/VoLTE, MMS, XCAP — no INTERNET, no
|
||||
// NOT_RESTRICTED) refuse with EPERM permanently, VPN or no VPN. Counting them as a
|
||||
// constraint is what made a phone with VoLTE report "measurement blocked" forever
|
||||
// and forced every run INCONCLUSIVE. They are recorded in networks[] as
|
||||
// app_usable:false; they are not something a run failed to do.
|
||||
if (e.model.appUsable == false) continue
|
||||
val err = try {
|
||||
DatagramSocket().use { s -> e.handle.bindSocket(s) }
|
||||
null
|
||||
|
||||
@@ -65,6 +65,20 @@ object NetworkInventory {
|
||||
/** @SystemApi NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY, API 28+. */
|
||||
private const val NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24
|
||||
|
||||
/**
|
||||
* Can an ordinary app send on this network?
|
||||
*
|
||||
* The carrier's special-purpose networks (IMS/VoLTE, MMS, XCAP) sit in `allNetworks` next to
|
||||
* the real ones, carrying `IMS`/`MMS` but neither `INTERNET` nor `NOT_RESTRICTED`. Binding
|
||||
* to them fails with EPERM forever, because it needs `CONNECTIVITY_USE_RESTRICTED_NETWORKS`
|
||||
* — signature-level, unobtainable for this app. Asking the capabilities up front is what
|
||||
* separates "the OS will never let us measure this" from "something is blocking us", which
|
||||
* a bind attempt alone cannot distinguish and which the constraint logic must not confuse.
|
||||
*/
|
||||
private fun appUsable(caps: NetworkCapabilities): Boolean =
|
||||
caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
|
||||
caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
|
||||
|
||||
private fun toModel(id: String, caps: NetworkCapabilities, lp: LinkProperties): MNetwork {
|
||||
val transport = when {
|
||||
caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> Transport.WIFI
|
||||
@@ -99,6 +113,7 @@ object NetworkInventory {
|
||||
id = id, transport = transport, iface = lp.interfaceName,
|
||||
link = Link(mtu = lp.mtu.takeIf { it > 0 }, addresses = addresses, routes = routes, dns = dns),
|
||||
systemVerdict = systemVerdict(caps),
|
||||
appUsable = appUsable(caps),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user