11"""
22KingWatch Pro v17 - core/network.py
3- Keys: dl, ul, sig, arc (matches main.py exactly)
3+
4+ HYBRID BAND DETECTION MATRIX:
5+ Layer 1: TelephonyManager.getDataNetworkType() (primary, most accurate)
6+ Layer 2: NetworkCapabilities.getLinkDownstreamBandwidthKbps() (no permission)
7+ Layer 3: Heuristic from actual measured speed (always works)
8+ Layer 4: sysfs interface name fallback
9+
10+ Band is determined by best available source, updated every 5s.
11+ Returns keys: dl, ul, sig, arc
412"""
513import time as _time
614import glob
1422_EMA = 0.4
1523_band_bps = 10.0 * 125000
1624
25+ # Speed history for heuristic (last 10 samples in bytes/s)
26+ _speed_hist = []
27+
1728
1829def _quality (dbm ):
1930 if - 50 < dbm : return "Excellent"
@@ -23,6 +34,64 @@ def _quality(dbm):
2334 return "Poor"
2435
2536
37+ def _heuristic_band (dl_bps ):
38+ """
39+ Layer 3: Classify band from actual measured speed.
40+ Uses median of recent speed history for stability.
41+ """
42+ global _band_bps
43+ if len (_speed_hist ) < 3 :
44+ return ""
45+ samples = sorted (_speed_hist )
46+ med = samples [len (samples ) // 2 ]
47+ mbps = med / 125000.0
48+ if 50.0 < mbps : _band_bps = 500.0 * 125000 ; return "5G (speed)"
49+ if 10.0 < mbps : _band_bps = 50.0 * 125000 ; return "4G LTE (speed)"
50+ if 1.0 < mbps : _band_bps = 14.0 * 125000 ; return "3G (speed)"
51+ if 0.05 < mbps : _band_bps = 2.0 * 125000 ; return "2G (speed)"
52+ return ""
53+
54+
55+ def _telephony_band (ctx , Ctx ):
56+ """Layer 1: TelephonyManager - most accurate when available."""
57+ global _band_bps
58+ try :
59+ from jnius import autoclass # type: ignore
60+ TM = autoclass ("android.telephony.TelephonyManager" )
61+ tm = getattr (ctx , 'getSystemService' )(getattr (Ctx , 'TELEPHONY_SERVICE' ))
62+ nt = getattr (tm , 'getDataNetworkType' )()
63+ if nt in (0 ,): return "" # UNKNOWN - try next layer
64+ if nt in (20 ,): _band_bps = 500.0 * 125000 ; return "5G NR Max 500Mbps"
65+ if nt in (13 ,): _band_bps = 50.0 * 125000 ; return "4G LTE Max 50Mbps"
66+ if nt in (19 ,): _band_bps = 150.0 * 125000 ; return "4G LTE-CA Max 150Mbps"
67+ if nt in (15 ,): _band_bps = 42.0 * 125000 ; return "4G HSPA+ Max 42Mbps"
68+ if nt in (8 ,9 ,10 ,3 ,5 ,6 ,12 ,14 ):
69+ _band_bps = 14.0 * 125000 ; return "3G Max 14Mbps"
70+ if nt in (1 ,2 ,4 ,7 ,11 ,16 ):
71+ _band_bps = 0.2 * 125000 ; return "2G Max 0.2Mbps"
72+ _band_bps = 10.0 * 125000 ; return "Mobile"
73+ except Exception :
74+ pass
75+ return ""
76+
77+
78+ def _caps_band (caps ):
79+ """Layer 2: NetworkCapabilities bandwidth estimate - no permission needed."""
80+ global _band_bps
81+ try :
82+ _gDL = getattr (caps , 'getLinkDownstreamBandwidthKbps' )
83+ dl = _gDL ()
84+ if 0 < dl :
85+ if 50000 < dl : _band_bps = 500.0 * 125000 ; return "5G ~" + str (dl // 1000 ) + "Mbps"
86+ if 5000 < dl : _band_bps = 50.0 * 125000 ; return "4G LTE ~" + str (dl // 1000 ) + "Mbps"
87+ if 1000 < dl : _band_bps = 20.0 * 125000 ; return "4G ~" + str (dl // 1000 ) + "Mbps"
88+ if 200 < dl : _band_bps = 14.0 * 125000 ; return "3G ~" + str (dl // 1000 ) + "Mbps"
89+ _band_bps = 0.2 * 125000 ; return "2G ~" + str (dl ) + "Kbps"
90+ except Exception :
91+ pass
92+ return ""
93+
94+
2695def _detect ():
2796 global _band_bps
2897 try :
@@ -39,44 +108,43 @@ def _detect():
39108 return _fallback ()
40109 _hT = getattr (caps , 'hasTransport' )
41110
42- if _hT (1 ): # WiFi
111+ # WiFi
112+ if _hT (1 ):
43113 try :
44114 wm = getattr (ctx , 'getSystemService' )(getattr (Ctx , 'WIFI_SERVICE' ))
45115 info = getattr (wm , 'getConnectionInfo' )()
46116 rssi = getattr (info , 'getRssi' )()
47117 freq = getattr (info , 'getFrequency' )()
48118 spd = getattr (info , 'getLinkSpeed' )()
49119 if 5000 < freq :
50- band = "5GHz"
51- _band_bps = 300.0 * 125000
120+ _band_bps = 300.0 * 125000 ; band = "5GHz"
52121 else :
53- band = "2.4GHz"
54- _band_bps = 100.0 * 125000
122+ _band_bps = 100.0 * 125000 ; band = "2.4GHz"
55123 q = _quality (rssi )
56124 return "WiFi " + band + " " + q + " " + str (rssi ) + "dBm " + str (spd ) + "Mbps"
57125 except Exception :
58- _band_bps = 100.0 * 125000
59- return "WiFi"
126+ _band_bps = 100.0 * 125000 ; return "WiFi"
60127
61- if _hT (0 ): # Cellular
62- try :
63- TM = autoclass ("android.telephony.TelephonyManager" )
64- tm = getattr (ctx , 'getSystemService' )(getattr (Ctx , 'TELEPHONY_SERVICE' ))
65- nt = getattr (tm , 'getDataNetworkType' )()
66- if nt in (20 ,): _band_bps = 500.0 * 125000 ; return "5G NR Max 500Mbps"
67- if nt in (13 ,): _band_bps = 50.0 * 125000 ; return "4G LTE Max 50Mbps"
68- if nt in (19 ,): _band_bps = 150.0 * 125000 ; return "4G LTE-CA Max 150Mbps"
69- if nt in (15 ,): _band_bps = 42.0 * 125000 ; return "4G HSPA+ Max 42Mbps"
70- if nt in (8 ,9 ,10 ,3 ,5 ,6 ,12 ,14 ):
71- _band_bps = 14.0 * 125000 ; return "3G Max 14Mbps"
72- if nt in (1 ,2 ,4 ,7 ,11 ,16 ):
73- _band_bps = 0.2 * 125000 ; return "2G Max 0.2Mbps"
74- _band_bps = 10.0 * 125000 ; return "Mobile"
75- except Exception :
76- _band_bps = 10.0 * 125000 ; return "Mobile"
128+ # Cellular - hybrid matrix
129+ if _hT (0 ):
130+ # Layer 1: TelephonyManager
131+ result = _telephony_band (ctx , Ctx )
132+ if result :
133+ return result
134+ # Layer 2: NetworkCapabilities bandwidth
135+ result = _caps_band (caps )
136+ if result :
137+ return result
138+ # Layer 3: Heuristic from speed history
139+ result = _heuristic_band (_dl )
140+ if result :
141+ return result
142+ # All failed - show Mobile
143+ _band_bps = 10.0 * 125000
144+ return "Mobile"
77145
78- if _hT (3 ): # Ethernet
79- _band_bps = 1000.0 * 125000
146+ if _hT (3 ):
147+ _band_bps = 1000.0 * 125000
80148 return "Ethernet Max 1000Mbps"
81149
82150 return "Connected"
@@ -91,16 +159,14 @@ def _fallback():
91159 try :
92160 with open (p ) as f :
93161 if f .read ().strip () == "up" :
94- _band_bps = 100.0 * 125000
95- return "WiFi"
162+ _band_bps = 100.0 * 125000 ; return "WiFi"
96163 except Exception :
97164 pass
98165 for p in glob .glob ("/sys/class/net/rmnet*/operstate" ):
99166 try :
100167 with open (p ) as f :
101168 if f .read ().strip () == "up" :
102- _band_bps = 10.0 * 125000
103- return "Mobile"
169+ _band_bps = 10.0 * 125000 ; return "Mobile"
104170 except Exception :
105171 pass
106172 return ""
@@ -127,16 +193,13 @@ def _bytes():
127193 return int (rx ), int (tx )
128194 except Exception :
129195 pass
130- rx = 0
131- tx = 0
196+ rx = 0 ; tx = 0
132197 try :
133198 with open ("/proc/net/dev" ) as f :
134199 for line in f .readlines ()[2 :]:
135200 p = line .split ()
136- if len (p ) < 10 :
137- continue
138- if p [0 ].startswith ("lo" ):
139- continue
201+ if len (p ) < 10 : continue
202+ if p [0 ].startswith ("lo" ): continue
140203 rx = rx + int (p [1 ])
141204 tx = tx + int (p [9 ])
142205 except Exception :
@@ -146,15 +209,12 @@ def _bytes():
146209
147210def _fmt (b ):
148211 kb = b / 1024
149- if not (kb < 1024 ):
150- return str (round (kb / 1024 , 1 )) + " MB/s"
151- if not (kb < 1 ):
152- return str (int (kb )) + " KB/s"
212+ if not (kb < 1024 ): return str (round (kb / 1024 , 1 )) + " MB/s"
213+ if not (kb < 1 ): return str (int (kb )) + " KB/s"
153214 return "0 KB/s"
154215
155216
156217def get_network ():
157- # Returns exactly 4 keys: dl, ul, sig, arc
158218 global _started , _dl , _ul
159219 if not _started :
160220 _started = True
@@ -185,7 +245,6 @@ def get_network():
185245 dl_raw = (rx - prx ) / dt
186246 else :
187247 dl_raw = 0.0
188-
189248 if ptx < tx :
190249 ul_raw = (tx - ptx ) / dt
191250 else :
@@ -194,6 +253,12 @@ def get_network():
194253 _dl = _EMA * dl_raw + (1 - _EMA ) * _dl
195254 _ul = _EMA * ul_raw + (1 - _EMA ) * _ul
196255
256+ # Feed speed history for heuristic
257+ if 0 < dl_raw :
258+ _speed_hist .append (dl_raw )
259+ if not (len (_speed_hist ) < 11 ):
260+ _speed_hist .pop (0 )
261+
197262 if 0 < _band_bps :
198263 arc = min (100.0 , _dl / _band_bps * 100 )
199264 else :
0 commit comments