Skip to content

Commit 5d22680

Browse files
xaionaro@dx.centerxaionaro@dx.center
authored andcommitted
fix: softap_tether_offload example replaces vendor offload HAL with system APIs
Replace android.hardware.tetheroffload.IOffload vendor HAL with INetworkManagementService system service and /proc/net/dev traffic stats. Shows network interfaces, tethering status, bandwidth control, and per-interface traffic counters.
1 parent 046e7da commit 5d22680

1 file changed

Lines changed: 114 additions & 33 deletions

File tree

  • examples/softap_tether_offload
Lines changed: 114 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
// Query tethering offload statistics from the hardware offload HAL.
1+
// Query tethering and network interface status via INetworkManagementService.
22
//
3-
// When SoftAP is active, the tethering offload HAL accelerates packet
4-
// forwarding in hardware. This example shows how to query forwarded
5-
// traffic statistics for each upstream interface.
3+
// Uses the "network_management" system service to list network interfaces,
4+
// check tethering status, and query interface configurations.
5+
// Replaces the previous vendor offload HAL approach with system-level APIs.
6+
//
7+
// Note: Some tethering methods were removed in Android API 36+.
68
//
79
// Build:
810
//
9-
// GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o build/softap_tether_offload ./examples/softap_tether_offload/
11+
// GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o build/softap_tether_offload ./examples/softap_tether_offload/
1012
// adb push softap_tether_offload /data/local/tmp/ && adb shell /data/local/tmp/softap_tether_offload
1113
package main
1214

@@ -15,10 +17,9 @@ import (
1517
"fmt"
1618
"os"
1719

20+
genOs "github.com/xaionaro-go/binder/android/os"
1821
"github.com/xaionaro-go/binder/binder"
1922
"github.com/xaionaro-go/binder/binder/versionaware"
20-
genOs "github.com/xaionaro-go/binder/android/os"
21-
"github.com/xaionaro-go/binder/android/hardware/tetheroffload"
2223
"github.com/xaionaro-go/binder/kernelbinder"
2324
"github.com/xaionaro-go/binder/servicemanager"
2425
)
@@ -41,43 +42,123 @@ func main() {
4142

4243
sm := servicemanager.New(transport)
4344

44-
// First list network interfaces to know which upstreams exist.
45-
netSvc, err := sm.GetService(ctx, servicemanager.NetworkmanagementService)
45+
svc, err := sm.GetService(ctx, servicemanager.NetworkmanagementService)
4646
if err != nil {
47-
fmt.Fprintf(os.Stderr, "get network_management: %v\n", err)
48-
} else {
49-
netMgr := genOs.NewNetworkManagementServiceProxy(netSvc)
47+
fmt.Fprintf(os.Stderr, "get network_management service: %v\n", err)
48+
os.Exit(1)
49+
}
50+
51+
netMgr := genOs.NewNetworkManagementServiceProxy(svc)
5052

51-
ifaces, err := netMgr.ListInterfaces(ctx)
52-
if err != nil {
53-
fmt.Fprintf(os.Stderr, "ListInterfaces: %v\n", err)
54-
} else {
55-
fmt.Printf("Network interfaces: %v\n\n", ifaces)
53+
// List all network interfaces.
54+
ifaces, err := netMgr.ListInterfaces(ctx)
55+
if err != nil {
56+
fmt.Fprintf(os.Stderr, "ListInterfaces: %v\n", err)
57+
} else {
58+
fmt.Printf("Network interfaces (%d):\n", len(ifaces))
59+
for _, iface := range ifaces {
60+
fmt.Printf(" %s\n", iface)
5661
}
5762
}
5863

59-
// Try to access the tethering offload HAL.
60-
offloadSvc, err := sm.GetService(ctx, servicemanager.ServiceName("android.hardware.tetheroffload.IOffload/default"))
64+
// Check tethering status (may be removed in API 36+).
65+
fmt.Println()
66+
tethering, err := netMgr.IsTetheringStarted(ctx)
67+
if err != nil {
68+
fmt.Fprintf(os.Stderr, "IsTetheringStarted: %v\n", err)
69+
fmt.Fprintf(os.Stderr, " (this method was removed in Android API 36)\n")
70+
} else {
71+
fmt.Printf("Tethering active: %v\n", tethering)
72+
}
73+
74+
// List tethered interfaces (may be removed in API 36+).
75+
tethered, err := netMgr.ListTetheredInterfaces(ctx)
76+
if err != nil {
77+
fmt.Fprintf(os.Stderr, "ListTetheredInterfaces: %v\n", err)
78+
fmt.Fprintf(os.Stderr, " (this method was removed in Android API 36)\n")
79+
} else if len(tethered) == 0 {
80+
fmt.Println("Tethered interfaces: (none)")
81+
} else {
82+
fmt.Printf("Tethered interfaces: %v\n", tethered)
83+
}
84+
85+
// Check bandwidth control.
86+
fmt.Println()
87+
bwCtrl, err := netMgr.IsBandwidthControlEnabled(ctx)
88+
if err != nil {
89+
fmt.Fprintf(os.Stderr, "IsBandwidthControlEnabled: %v\n", err)
90+
} else {
91+
fmt.Printf("Bandwidth control enabled: %v\n", bwCtrl)
92+
}
93+
94+
// Check IP forwarding (may be removed in API 36+).
95+
fwd, err := netMgr.GetIpForwardingEnabled(ctx)
96+
if err != nil {
97+
fmt.Fprintf(os.Stderr, "GetIpForwardingEnabled: %v\n", err)
98+
} else {
99+
fmt.Printf("IP forwarding enabled: %v\n", fwd)
100+
}
101+
102+
// Try to read network statistics from procfs as a complement.
103+
fmt.Println()
104+
printNetStats(ifaces)
105+
}
106+
107+
// printNetStats reads basic traffic counters from /proc/net/dev for the
108+
// given interfaces.
109+
func printNetStats(ifaces []string) {
110+
data, err := os.ReadFile("/proc/net/dev")
61111
if err != nil {
62-
fmt.Fprintf(os.Stderr, "get tether offload HAL: %v\n", err)
63-
fmt.Fprintf(os.Stderr, "(offload HAL not available — no hardware offload support or SELinux denial)\n")
64-
fmt.Fprintf(os.Stderr, "\nOn devices with offload support, this queries forwarded traffic stats:\n")
65-
fmt.Fprintf(os.Stderr, " offload.GetForwardedStats(ctx, \"wlan0\") → {RxBytes, TxBytes}\n")
66-
os.Exit(0)
112+
fmt.Fprintf(os.Stderr, "read /proc/net/dev: %v\n", err)
113+
return
67114
}
68115

69-
offload := tetheroffload.NewOffloadProxy(offloadSvc)
116+
// Build a set of interfaces we care about.
117+
ifaceSet := make(map[string]bool, len(ifaces))
118+
for _, iface := range ifaces {
119+
ifaceSet[iface] = true
120+
}
70121

71-
// Query forwarded stats for common upstream interfaces.
72-
upstreams := []string{"wlan0", "eth0", "rmnet0", "rmnet_data0"}
122+
fmt.Println("Network traffic statistics:")
123+
fmt.Printf(" %-15s %15s %15s\n", "Interface", "RX bytes", "TX bytes")
124+
fmt.Printf(" %-15s %15s %15s\n", "---------", "--------", "--------")
73125

74-
fmt.Println("Tethering offload forwarded traffic:")
75-
for _, iface := range upstreams {
76-
stats, err := offload.GetForwardedStats(ctx, iface)
77-
if err != nil {
126+
lines := splitLines(string(data))
127+
for _, line := range lines[2:] { // skip header lines
128+
var iface string
129+
var rxBytes, txBytes int64
130+
var dummy int64
131+
// Format: iface: rx_bytes rx_packets ... tx_bytes tx_packets ...
132+
n, _ := fmt.Sscanf(line, " %s %d %d %d %d %d %d %d %d %d",
133+
&iface, &rxBytes, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &txBytes)
134+
if n < 10 {
78135
continue
79136
}
80-
fmt.Printf(" %-15s RX: %d bytes TX: %d bytes\n",
81-
iface, stats.RxBytes, stats.TxBytes)
137+
// Remove trailing colon from iface name.
138+
if len(iface) > 0 && iface[len(iface)-1] == ':' {
139+
iface = iface[:len(iface)-1]
140+
}
141+
if !ifaceSet[iface] {
142+
continue
143+
}
144+
if rxBytes == 0 && txBytes == 0 {
145+
continue
146+
}
147+
fmt.Printf(" %-15s %15d %15d\n", iface, rxBytes, txBytes)
148+
}
149+
}
150+
151+
func splitLines(s string) []string {
152+
var lines []string
153+
start := 0
154+
for i := 0; i < len(s); i++ {
155+
if s[i] == '\n' {
156+
lines = append(lines, s[start:i])
157+
start = i + 1
158+
}
159+
}
160+
if start < len(s) {
161+
lines = append(lines, s[start:])
82162
}
163+
return lines
83164
}

0 commit comments

Comments
 (0)