24 Commits

Author SHA1 Message Date
Sing Dev
d4b55a7482 Increase speed test timeout to 10s (VLESS Reality nodes need 2-5s for full handshake) 2026-04-06 02:05:11 +08:00
Sing Dev
f952911bfa Fix speed test: use /proxies/{name}/delay directly, update UI on main thread
- LaunchedEffect and test button both use refreshAllStreaming now
- onResult callback dispatches to Main thread for Compose state update
- Removed broken testAll() path (mini-sing doesn't populate proxy history)
- Bottom sheet test latency also uses streaming approach
2026-04-06 01:51:07 +08:00
Sing Dev
f923340845 Streaming speed test: each node result updates UI immediately instead of waiting for all 2026-04-06 01:48:03 +08:00
Sing Dev
a135de421a Fix NodeTester: parse group delay response directly, use HTTP test URL, increase timeout 2026-04-06 01:46:37 +08:00
Sing Dev
6903f6aaab Replace fork+exec speed test with in-process mini-sing helper
- Remove libsingbox.so and libforkexec.so entirely
- Add MiniSing.startHelper/stopHelper/isHelperRunning JNI functions
- Add helperBox in ffi_android.go — second Box instance for testing
- NodeTester now uses MiniSing.startHelper() instead of ForkExec
- Single native library (libminising.so) handles both VPN and testing
2026-04-06 01:35:03 +08:00
Sing Dev
58252426d5 Fix NodeTester: revert to libsingbox.so, add 'cn' DNS server tag for compatibility 2026-04-06 01:28:48 +08:00
Sing Dev
1d3c426546 Show per-node loading spinner during speed test 2026-04-06 01:24:31 +08:00
Sing Dev
536d3abfda NodeTester: use libminising.so instead of libsingbox.so for speed test helper 2026-04-06 01:21:16 +08:00
Sing Dev
f2617acbb5 Fix stability, security, and data integrity issues
- switchNode: rollback to previous config on failure instead of full disconnect
- NodeTester: synchronized start/stop to prevent race conditions
- CommandReceiver: set exported=false to prevent external broadcast abuse
- GeneralSettingsScreen: remove redundant onDispose save (each control saves immediately)
2026-04-06 01:10:55 +08:00
Sing Dev
595d8811d3 Add unified event bus for VPN state, notifications, and error feedback
- VpnEvent sealed class + VpnEventBus (SharedFlow for one-shot events,
  StateFlow for persistent connection state)
- SingVpnService: emit events instead of broadcasts, failVpn(reason)
  with specific error messages, notification with Disconnect button
  and periodic traffic stats update
- MainActivity: SnackbarHost consumes events (errors, sync results,
  node switches), removed BroadcastReceiver
- OverviewScreen: StateFlow-driven instead of 1s polling
- NodesScreen: speed test loading indicator (CircularProgressIndicator)
- ProfilesScreen: sync results via event bus with error distinction
2026-04-05 01:04:19 +08:00
Sing Dev
82cf403656 Reduce battery drain: default log level to warn, lifecycle-aware polling
- Log level changed from trace to warn (configurable in Settings)
- Helper process log level also set to warn
- All UI polling (traffic stats, connections, logs) now uses
  repeatOnLifecycle(STARTED) — pauses when screen not visible
2026-04-05 00:12:56 +08:00
Sing Dev
c53d943150 Add unified rule management with rule set catalog and smart dedup
- New RulesScreen with two tabs: Custom Rules (structured CRUD with
  type/pattern/outbound, enable/disable, filter chips) and Rule Sets
  (28 built-in + remote URL support, per-service toggle)
- Rule sets now loaded natively by engine via rule_set declarations
  instead of Kotlin-side inline expansion — simpler and more efficient
- Remote rule sets downloaded through proxy (download_detour: "proxy")
- ProxyProtocol enum as single source of truth for supported protocols,
  fixes NodeTester excluding hysteria2/tuic/wireguard/socks5
- Quick Rule auto-detects type (IP_CIDR, DOMAIN_SUFFIX, DOMAIN_KEYWORD)
- Logs screen: add open/share button via FileProvider
- Build time shown in settings
2026-04-04 05:00:40 +08:00
Sing Dev
bfc5b675e3 Add quick rule: long-press connection to route domain to Direct/Proxy/Block
Long-pressing a connection in the Active Connections screen shows a
bottom sheet to quickly add the domain to direct/proxy/block rules.
Extracts the registrable domain from the host (e.g. cdn.example.com
→ example.com) and appends it to the corresponding rule list in
SharedPreferences. Duplicate detection prevents re-adding.
2026-04-03 06:42:13 +08:00
Sing Dev
1106f61cf0 Add CommandReceiver for adb control, fix tailscale routing
- CommandReceiver: exported broadcast receiver for adb CLI control
  - start/stop/status/set_config commands
  - Usage: adb shell am broadcast -a com.sing.vpn.CMD -n com.sing.vpn/.CommandReceiver --es cmd start
- SingConfig: add controlplane.tailscale.com → direct route rule
  to prevent tailnet control traffic from going through proxy
2026-04-02 15:31:15 +08:00
Sing Dev
3b7097f5ee Add E2E test suite for Android VPN
Shell-based test framework using adb + uiautomator dump.
Avoids instrumented tests which crash due to Go c-shared
native lib init in test runner process.

Tests:
- test01: App launch, UI state verification
- test02: VPN connect/disconnect lifecycle
- test03: Clash API connectivity after connect
- test04: Tailscale peer discovery (soft-pass on emulator)
- test05: Reconnect after disconnect

Usage: ./test-e2e.sh [all|test01|test02|...]
2026-04-02 06:24:09 +08:00
Sing Dev
a5ba9ce4f6 Remove dead ts-dns upstream, wire accept_routes to Tailscale config
- Remove ts-dns UDP upstream (100.100.100.100) — .ts.net DNS is handled
  by resolver.Exchange() via LookupTailscale, never reaches mini-dns.
- Write accept_routes into Tailscale outbound JSON config so the
  Settings toggle actually takes effect.
2026-04-02 05:49:34 +08:00
Sing Dev
ed8503f77a Remove hardcoded node credentials, extract shared formatBytes utility
- NodeStore: remove default nodes with real server credentials,
  start with empty list for user to add their own.
- Extract duplicate formatBytes() from HomeFragment and
  ConnectionsFragment into shared Util.kt.
2026-04-02 05:42:50 +08:00
Sing Dev
789c0947ad Fix TUN DNS hijack and fd lifecycle for mini-sing
- addDnsServer: 172.19.0.1 → 198.18.0.1 (TUN local address can't be
  DNS target — gvisor doesn't route packets to self through the stack)
- duplicateTunFdForEngine: dup() fd for Go so Java and Go each own
  their fd independently, no double-close on stop/switch
- SingConfig: add auto_route/strict_route/exclude_outbound, rule set
  coverage dedup, ads block opt-in toggle, PreferIPv4
- NodeTester: filter unsupported outbound types, fix interval type
2026-04-02 02:41:59 +08:00
Sing Dev
60b3f7c04d Snapshot Android app state 2026-04-02 00:23:14 +08:00
Sing Dev
d384ab8188 Finalize node switch: SIGKILL+restart (no sing-tun patch needed)
SIGHUP hot-reload won't work with binary mode because:
- Java-side dup'd fd exists only in parent process, not in child
- sing-box child process can't see fds created after fork

SIGKILL+restart approach is reliable and fast (~5ms gap):
- SIGKILL skips graceful shutdown, so fd is never closed by child
- TUN fd owned by VpnService (Java), survives child restarts
- Kernel TUN queue buffers packets during the gap

Removed: Os.dup() complexity, reflection for fd int extraction.
Simplified SingVpnService to clean SIGKILL+restart pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 18:49:23 +00:00
Sing Dev
ee81a8cac7 Fix node switch: use SIGKILL+restart instead of SIGHUP hot-reload
SIGHUP hot-reload doesn't work with file_descriptor mode because
sing-box closes the TUN fd when tearing down the old tun inbound,
making it unavailable for the new instance.

Switch to fast SIGKILL+restart approach:
- SIGKILL old process (instant, no graceful shutdown delay)
- Start new process with same TUN fd (owned by VpnService, stays open)
- Total switch time ~5ms, packets buffered in kernel TUN queue

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 18:30:13 +00:00
Sing Dev
365f743741 v0.3: Helper process for node speed testing + A/B swap + DNS fix
Helper process (NodeTester):
- sing-box helper runs at app launch with mixed inbound + Clash API (:9090)
- All nodes configured as urltest group for auto speed testing
- Delay results shown in Nodes tab (green <300ms / orange <600ms / red)
- Queries via Clash REST API GET /proxies

A/B node swap:
- ACTION_SWITCH in SingVpnService: kill old sing-box, start new with same TUN fd
- NodesFragment triggers A/B swap when selecting node while VPN is connected
- ~200ms gap, packets buffered in kernel TUN queue

DNS hijack-dns fix:
- Add route rule {"action": "sniff"} before {"protocol": "dns", "action": "hijack-dns"}
- Required for sing-box 1.13+ where inbound sniff field is deprecated
- Fallback port 53 hijack rule for safety

Other (from previous worker):
- Splash screen, Reality/uTLS support, dual DNS (DoT remote + UDP local)
- 3 default nodes (HK01 VLESS, JMS SS, SOCKS5)
- network_security_config.xml for localhost cleartext HTTP (Clash API)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 18:22:01 +00:00
Sing Dev
b204db589a Remove tun2socks: sing-box now reads TUN fd directly
- Patch sing-box source to add `file_descriptor` field to tun inbound config
  (2 lines changed in option/tun.go + protocol/tun/inbound.go)
- Rebuild sing-box binary with fd:// support (android/arm64, full tags)
- SingVpnService: single-process architecture (sing-box only, no tun2socks)
  - Create TUN first, pass fd to sing-box config, start sing-box with keepFd=tunFd
- SingBoxConfig: switch from mixed inbound to tun inbound with file_descriptor
- Startup time improved: 0.166s vs 0.88s+tun2socks

Architecture: TUN fd → sing-box (tun inbound fd://N → outbound) → upstream
Tested: HTTPS works, exit IP = 156.225.28.220, single process confirmed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 16:11:15 +00:00
Sing Dev
d63dbf4ab0 Sing v0.2: multi-page navigation + sing-box engine
- Add 4-tab navigation (Home/Nodes/Logs/Settings) with BottomNavigationView
- Replace tun2socks direct proxy with sing-box engine for multi-protocol support
  (socks5, shadowsocks, vmess, vless)
- Dual-process architecture: sing-box (mixed inbound :10801) + tun2socks (TUN fd bridge)
- Add node management: NodeStore (SharedPreferences), add/delete/select nodes
- Add SingBoxConfig: dynamic sing-box JSON config generation (1.13+ format)
- Add real-time log viewer with incremental reading
- Add settings page (DNS, auto-connect, about)
- Migrate to AppCompatActivity + MaterialComponents theme
- Fix fork_exec.c: guard keepFd=-1 for sing-box (no TUN fd needed)
- Delete unused files: TcpProxy, Socks5Client, ConfigReader, config.jsonc

Tested: VPN connects, exit IP = 156.225.28.220, HTTPS works, all tabs functional.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 15:56:57 +00:00