Skip to content

[Community] Android Kotlin port — tg-ws-proxy-android #765

@ComradeSwarog

Description

@ComradeSwarog

Hi @Flowseal,

First of all — thank you for the excellent tg-ws-proxy and the clever DPI-bypass architecture you built. It works incredibly well on desktop.

We wanted to bring the same functionality to Android, so we created a Kotlin port that runs as a foreground service with a native UI:

Repository: https://github.com/ComradeSwarog/tg-ws-proxy-android
License: MIT (same as upstream)
Language: Kotlin / Android SDK 34


What was ported

Original Python Kotlin/Android
proxy/tg_ws_proxy.py TgWsProxy.kt
proxy/raw_websocket.py RawWebSocket.kt
proxy/doh_resolver.py DoHResolver.kt
proxy/balancer.py Balancer.kt
proxy/fake_tls.py handleFakeTLS, FakeTlsInputStream
proxy/stats.py ProxyStats.kt

Stability fixes discovered during real-world testing

We tested the port extensively on Samsung devices under active DPI (1+ hour continuous sessions) and fixed several issues that also likely affect the Python version:

Issue Cause Fix
App crash on connect ConcurrentHashMap.put(null) in connectParallel() when raw socket connect fails Null-guard before map insertion
Service killed / restart loop ForegroundServiceDidNotStartInTimeException on Android 12+ sticky restart Moved startForeground() to the very top of onStartCommand()
Socket FD exhaustion Parallel connect left loser's sockets open Track Socket per-thread in ConcurrentHashMap<Thread, Socket> and close all losers after winner is chosen
Global SSL factory poisoning DoHResolver mutated HttpsURLConnection.setDefaultSSLSocketFactory(...) Isolated SSLContext per-connection, never touching global defaults
Client read timeout soTimeout = 10s was shorter than MTProto handshake silence under DPI Raised to 30s
CF hammering / 429 rate-limit No backoff for rejected CF Workers Adaptive blacklist TTL (2min for 429, 5min for TCP refuse)

Additional features

  • Foreground service with persistent notification + optional background restart
  • GUI settings (Host, Port, Secret, DC:IP, bypass toggles) instead of config files
  • In-app log viewer with export/share to .txt (live buffer + file rotation)
  • Auto-generated tg://proxy link with one-tap "Open in Telegram" button
  • Connection pool with warmup, refill, and age-based eviction
  • Pre-warmed CF pool — tests CF fallback domains before first real connection

Attribution

Please feel free to mention tg-ws-proxy-android in your README if you find it useful. We'd also love feedback or cross-referencing if anything here is helpful for the Python upstream.

Once again — thanks for the original idea and the clean architecture! 🙏

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions