{"id":45921732,"url":"https://github.com/hightemp/proxy_switcher","last_synced_at":"2026-04-03T16:01:40.348Z","repository":{"id":325600346,"uuid":"1101761322","full_name":"hightemp/proxy_switcher","owner":"hightemp","description":"A simple and efficient Android application that runs a local HTTP proxy server on your device, allowing you to route your traffic through various upstream proxies (HTTP, HTTPS, SOCKS5).","archived":false,"fork":false,"pushed_at":"2026-02-28T11:13:56.000Z","size":961,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-28T11:42:50.811Z","etag":null,"topics":["android","android-proxy","http-proxy","kotlin","proxy","proxy-rotation"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hightemp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-22T07:23:08.000Z","updated_at":"2026-02-28T11:13:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/hightemp/proxy_switcher","commit_stats":null,"previous_names":["hightemp/proxy_switcher"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/hightemp/proxy_switcher","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hightemp%2Fproxy_switcher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hightemp%2Fproxy_switcher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hightemp%2Fproxy_switcher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hightemp%2Fproxy_switcher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hightemp","download_url":"https://codeload.github.com/hightemp/proxy_switcher/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hightemp%2Fproxy_switcher/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31362682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T15:19:21.178Z","status":"ssl_error","status_checked_at":"2026-04-03T15:19:20.670Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","android-proxy","http-proxy","kotlin","proxy","proxy-rotation"],"created_at":"2026-02-28T08:53:38.025Z","updated_at":"2026-04-03T16:01:40.108Z","avatar_url":"https://github.com/hightemp.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Proxy Switcher\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"proxy_switcher_logox768.png\" width=\"100\" /\u003e\n\u003c/p\u003e\n\n![Version](https://img.shields.io/badge/version-1.0.3-blue)\n![Status](https://img.shields.io/badge/status-experimental-orange)\n![Vibe Coded](https://img.shields.io/badge/vibe-coded-blueviolet)\n\nAndroid application that runs a local HTTP proxy server on your device and routes all traffic through an upstream proxy (HTTP, HTTPS, or SOCKS5). Automatically manages the system-wide proxy setting so every application on the device uses it — no root required.\n\n## Features\n\n- **Local Proxy Server** — starts an HTTP proxy on port `8080`\n- **Upstream protocols** — HTTP, HTTPS (TLS), SOCKS5 with optional username/password auth\n- **Auto System Proxy** — sets `127.0.0.1:8080` as the device-wide proxy on START; restores the original on STOP (requires a one-time ADB grant)\n- **Race-safe START/STOP** — in-flight start job is cancelled before restoring proxy settings, preventing stale loopback from getting stuck\n- **Reliable proxy reset** — restores `http_proxy` to `:0` (not delete) so Chrome/Samsung stacks recognise the clear immediately\n- **System Proxy screen** — shows both `Settings.Global` and the actual `LinkProperties` proxy (what Chrome reads). Warns and links to WiFi settings when a per-network proxy is left behind\n- **Proxy Management** — add, edit, delete upstream proxies (stored in Room)\n- **Logs screen** — built-in real-time log viewer\n- **Foreground Service** — proxy keeps running when the app is in the background\n\n## Tech Stack\n\n| Layer | Technology |\n|---|---|\n| Language | Kotlin |\n| UI | Jetpack Compose (Material 3) |\n| Architecture | MVVM + Clean Architecture |\n| DI | Hilt |\n| Database | Room |\n| Networking | Custom socket-based proxy core |\n\n## Build \u0026 Install\n\n```bash\n# Build debug APK\n./gradlew assembleDebug\n\n# Build and install on connected device\n./gradlew installDebug\n# or\nmake install\n```\n\n### One-time ADB permission grant\n\nRequired for automatic system proxy management:\n\n```bash\nmake adb-grant\n# equivalent to:\nadb shell pm grant com.hightemp.proxy_switcher android.permission.WRITE_SECURE_SETTINGS\n```\n\n\u003e The permission survives app updates but is lost on full uninstall. Run the command again after reinstalling from scratch.\n\n## Usage\n\n1. Open the app → tap the gear icon → add an upstream proxy (host, port, type, auth)\n2. Select the proxy in the dropdown on the home screen\n3. Tap **START PROXY**\n\n**With `WRITE_SECURE_SETTINGS` granted** — the app automatically sets the system proxy and restores it on STOP. All device traffic (Chrome, system apps, etc.) is routed through the upstream.\n\n**Without the permission** — set the proxy manually once:  \nWiFi Settings → long-press active network → Modify → Advanced → Proxy: Manual → `127.0.0.1:8080` → Save\n\n## Makefile Targets\n\n```\nmake help             # list all targets with descriptions\nmake build-local      # ./gradlew assembleDebug\nmake install          # ./gradlew installDebug\nmake adb-grant        # grant WRITE_SECURE_SETTINGS\nmake adb-check-proxy  # show all proxy keys: Settings.Global + per-network WiFi\nmake adb-clear-proxy  # emergency: reset all proxy settings on device\nmake release          # bump VERSION → gradle → commit → tag → push (triggers CI)\nmake update-version   # update versionCode/versionName in build.gradle.kts only\nmake tag              # create and push git tag only\n```\n\n## ADB Debugging\n\n### Inspect current proxy state\n\n```bash\nmake adb-check-proxy\n# Shows Settings.Global keys AND the per-network WiFi proxy from dumpsys wifi\n```\n\n### Emergency reset\n\n```bash\nmake adb-clear-proxy\n```\n\nThis resets all `Settings.Global` proxy keys. It does **not** touch per-network WiFi proxy (see below).\n\n### Diagnosing `ERR_PROXY_CONNECTION_FAILED` after STOP\n\nAndroid proxy state lives in two separate places:\n\n| Source | Keys | Tool |\n|---|---|---|\n| `Settings.Global` | `http_proxy`, `global_http_proxy_*`, `global_proxy_pac_url` | `adb shell settings get global http_proxy` |\n| Per-network WiFi config | stored in `WifiConfiguration` | `adb shell dumpsys wifi \\| grep -i proxy` |\n\n**Common cause on Samsung / Android 10+**: `http_proxy` was not cleared properly (delete leaves residue in some ROMs). The app uses `putString(\"http_proxy\", \":0\")` to force a hard reset that Chrome and the Samsung network stack recognise correctly.\n\n**Per-network WiFi proxy**: If the WiFi proxy was set manually before you started using the app's auto-management, it persists independently of `Settings.Global`. On Android 10+ non-system apps cannot modify it programmatically.\n\n**Recovery steps**:\n\n1. Run `make adb-check-proxy` — look for `HTTP proxy: [127.0.0.1] 8080` in the WiFi section\n2. Open the **System Proxy** screen in the app (NetworkCheck icon on the home screen). If the per-network proxy is stale, a red warning and **Open WiFi Settings** button appear\n3. In WiFi Settings: long-press active network → Modify → Advanced → Proxy → **None** → Save\n4. Force-kill Chrome: `adb shell am force-stop com.android.chrome`\n\nAfter step 3 the issue will not recur as long as the app manages the system proxy automatically.\n\n### Stream proxy logs\n\n```bash\nadb logcat -s ProxyServer:D ProxyService:D\n```\n\n## Performance Characteristics\n\nThe proxy core (`ProxyServer.kt`) is tuned for mobile network conditions:\n\n- `TCP_NODELAY = true` — removes Nagle-algorithm delays (40–200 ms) on interactive/multiplexed traffic\n- `SO_RCVBUF / SO_SNDBUF = 256 KB` — larger kernel ring buffers reduce back-pressure during burst traffic (video, images)\n- 64 KB tunnel buffer — 16× larger than the original 4 KB, fewer `read/write` iterations on large transfers\n- No `flush()` in the tunnel hot path — `SocketOutputStream.flush()` is a no-op in JVM but adds call overhead in a tight loop\n- Bulk `readNBytes` — eliminates per-byte syscalls during SOCKS5 handshake\n- `BufferedInputStream` for SOCKS5 handshake — batches small reads\n- `ServerSocket` backlog = 256 — handles burst connection arrivals without OS dropping them\n- 16 KB initial request buffer — avoids truncating large HTTP headers (Cookie, Authorization)\n\n## Architecture Notes\n\n```\napp/src/main/java/com/hightemp/proxy_switcher/\n├── data/local/            # Room DB: AppDatabase, ProxyDao, ProxyEntity (ProxyType enum)\n├── data/repository/       # ProxyRepository — thin DAO wrapper\n├── di/                    # Hilt modules (DatabaseModule)\n├── proxy/                 # ProxyServer.kt — socket-based HTTP/HTTPS/SOCKS5 proxy core\n├── service/               # ProxyService.kt — foreground service, port 8080\n├── ui/\n│   ├── screens/           # HomeScreen, ProxyListScreen, AddEditProxyScreen, LogsScreen, SystemProxyScreen\n│   ├── theme/             # Material 3 tokens\n│   └── viewmodel/         # ProxyViewModel (@HiltViewModel)\n└── utils/                 # AppLogger — singleton StateFlow\u003cList\u003cString\u003e\u003e, capped at 1000 entries\n```\n\nKey `ProxyService` behaviours:\n- Saves all proxy keys to `SharedPreferences` on START, restores on STOP\n- Skips stale loopback values (`127.0.0.1:8080`) when saving originals — prevents a previous crash from polluting the restore target\n- Resets `http_proxy` to `:0` instead of deleting — reliable on Samsung/Chrome (SKILL: android-proxy-recovery)\n- Per-network WiFi proxy modified only on Android \u003c 10 (API 29) — restricted for non-system apps on newer versions\n\n## Screenshots\n\n\u003cimg src=\"screenshots/photo_2025-11-22_10-17-00.jpg\" width=\"19%\" /\u003e \u003cimg src=\"screenshots/photo_2025-11-22_10-17-08.jpg\" width=\"19%\" /\u003e \u003cimg src=\"screenshots/photo_2025-11-22_10-17-13.jpg\" width=\"19%\" /\u003e \u003cimg src=\"screenshots/photo_2025-11-22_10-17-18.jpg\" width=\"19%\" /\u003e \u003cimg src=\"screenshots/photo_2025-11-22_10-17-22.jpg\" width=\"19%\" /\u003e\n\n![](https://asdertasd.site/counter/proxy_switcher)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhightemp%2Fproxy_switcher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhightemp%2Fproxy_switcher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhightemp%2Fproxy_switcher/lists"}