Ever plugged your Android phone into your PC to figure out what's eating all your storage?
Here's what happens with the standard USB connection (MTP):
- You open the phone in File Explorer / Finder
- Click on a folder with lots of files
- "Calculating size..." β hangs for 4+ minutes
- Eventually shows sizes, but navigating is painfully slow
- Trying to find large files? Good luck scrolling through hundreds of folders one by one
This is because MTP (Media Transfer Protocol) β the protocol your OS uses to talk to Android over USB β was designed in 2008 for MP3 players. It transfers file metadata one item at a time, with no caching, no parallel requests, and no way to do a fast recursive scan. It was never built for phones with 100GB+ of photos, videos, and apps.
SocketSweep bypasses MTP entirely.
Full /sdcard scan on a Samsung Galaxy S24 Ultra (256GB) with ~47,000 files:
SocketSweep: ~6-15 seconds β full interactive treemap ready to explore.
Scan time varies depending on device load (background apps, media indexing, thermal state). Best case was 6.9 seconds with a warm cache and minimal background activity.
For comparison, doing the same thing over MTP (plugging in the phone and browsing via Windows Explorer or Finder) typically involves minutes of "Calculating size..." freezes, and macOS Finder doesn't even show folder sizes at all.
Proper side-by-side benchmarks against OpenMTP and other tools are coming soon.
| Platform | Download |
|---|---|
| πͺ Windows | Installer (.exe) Β· Enterprise (.msi) |
| π macOS (Apple Silicon) | Disk Image (.dmg) |
| π§ Linux | AppImage Β· .deb |
Arch/AUR note: The
socketsweep-binpackage is currently unmaintained and its checksum check fails until an adopter bumps it to v1.1.1. In the meantime, Arch users can install the .deb directly:sudo pacman -U SocketSweep_1.1.1_amd64.deb
macOS note: Since the build is ad-hoc signed, run this once after installing:
xattr -cr /Applications/SocketSweep.app
Go to Settings β About Phone β tap "Build Number" 7 times to unlock Developer Options. Then go to Settings β Developer Options β enable "USB Debugging".
- Connect your phone via USB cable
- Open SocketSweep
- Click Connect β the app will automatically push the daemon to your phone and set everything up
- Click Scan β your full storage treemap loads in seconds
- Click on any block in the treemap to drill down. Found something huge you don't need? Delete it right from the app.
That's it. No apps to install on your phone, no Wi-Fi setup, no root required.
Instead of going through MTP, SocketSweep does something completely different:
- Pushes a tiny C++ program (~1MB) to your phone via ADB
- That program scans the filesystem directly on the phone using native POSIX calls β this is why it's so fast (no MTP bottleneck)
- Streams the results back to your PC over a TCP socket through the USB cable
- Renders an interactive treemap in a React frontend so you can visually see what's taking space
The architecture was inspired by scrcpy β the "push a native binary via ADB, communicate over a local socket" pattern.
Each connection authenticates with a fresh 256-bit session secret before the
daemon accepts a command. The secret reaches the phone through a shell-owned
startup file with mode 0600; the daemon reads and removes that file before
listening. The app and daemon must use the same protocol version.
SocketSweep has three layers:
flowchart TB
subgraph Host["Host Desktop"]
UI["React + Recharts<br>Interactive Dashboard"]
Bridge["Rust / Tauri Backend<br>Command Orchestrator"]
UI <--> Bridge
end
subgraph Transport["ADB Protocol"]
Tunnel["ADB Port Forwarding<br>TCP:5050 -> TCP:5050"]
end
subgraph Device["Android Device"]
Daemon["C++17 Daemon<br>Headless Socket Server"]
Storage[("POSIX Filesystem<br>/sdcard")]
Daemon <--> Storage
end
Bridge <--> Tunnel
Tunnel <--> Daemon
sequenceDiagram
participant U as React UI
participant R as Rust (Tauri)
participant A as ADB Shell
participant D as C++ Daemon (Android)
%% Connect Phase
U->>R: invoke("init_daemon")
R->>A: pkill daemon (Cleanup)
R->>A: push daemon /data/local/tmp
R->>A: appops set MANAGE_EXTERNAL_STORAGE allow
R->>A: Write private startup secret, launch daemon
R->>A: adb forward tcp:5050 tcp:5050
R->>D: AUTH session secret (on every connection)
R->>D: Ping-Retry Loop (150ms)
D-->>R: ACK Connection
R-->>U: Connected!
%% Scan Phase
U->>R: invoke("run_scan", { path: "/sdcard" })
R->>D: TCP Send: `SCAN /sdcard\n`
Note over D: Recursive Fast POSIX Traversal
D-->>R: Stream Large JSON Tree
R-->>U: Parse & Render Treemap
%% Delete Phase
U->>R: invoke("delete_item", { path })
R->>D: TCP Send: `DELETE /sdcard/... \n`
Note over D: Descriptor-relative traversal and unlinkat<br/>Child symlinks are never followed
D-->>R: {"status":"ok"}
R-->>U: Update UI / Rescan
- Node.js (v18+)
- Rust (v1.70+ with Cargo)
- Android NDK (v26d or newer)
- Android SDK / ADB installed and added to your system
$PATH.
Cross-compile the daemon for aarch64-linux-android:
# Set your NDK path
export NDK=/path/to/your/android-ndk-r26d
# Build the daemon
cd engine
bash ./build.shThis generates the stripped daemon binary in the engine/ directory and
copies it into src-tauri/bin/daemon, which Tauri bundles. Rebuild after changing
the daemon so local development uses the current protocol.
cd ..
npm installnpm run tauri devEnsure your Android device is plugged in via USB and USB Debugging is enabled.
# macOS/Linux: compiles the host daemon and uses disposable local files
python3 -m unittest discover -s tests -v
# Build the frontend first when testing from a clean checkout
npm run build
cargo test --locked --manifest-path src-tauri/Cargo.toml --libThe line-based protocol preserves spaces in filenames. Paths containing a newline, carriage return, or null byte are rejected before sending, so they cannot cause a different file to be deleted. Scan responses containing invalid UTF-8 filenames are also rejected rather than replacing filename bytes.
Deletion is anchored to the directory opened for the successful scan. Child
directories are opened one component at a time without following symlinks, and
removal uses those handles instead of resolving the original full path again.
Detected changes to roots or directory entries stop deletion. Parent symlinks
and traversal into another filesystem are rejected. /sdcard root aliases remain supported.
Recursive deletion has a depth limit of 64 directories.
Deletion is not transactional: concurrent filesystem changes or permission errors can stop it after some entries were removed. Rescan before retrying. An operation already in progress uses the opened directory identities; renaming a path cannot redirect it into a different directory through a new symlink.
Android 11 introduced Scoped Storage, restricting file access. SocketSweep automatically tries to bypass this via:
adb shell appops set com.android.shell MANAGE_EXTERNAL_STORAGE allowIf scanning still shows nothing, check if your OEM requires extra toggles (e.g., Xiaomi needs "USB Debugging (Security settings)" enabled).
If you're on a Samsung device and USB Debugging is greyed out, you probably have Auto Blocker enabled. Go to Settings β Security β Auto Blocker and turn it off. Auto Blocker disables USB Debugging entirely, so no ADB-based tool (including SocketSweep) will work with it on. It's off by default β you'd only have this issue if you manually turned it on.
If the AppImage crashes or fails to start on Arch Linux under a Wayland session, launch it with the Wayland client library preloaded:
LD_PRELOAD=/usr/lib/libwayland-client.so ./SocketSweep.AppImageSee this issue for more context.
If you get Permission denied, make sure the daemon is being pushed to /data/local/tmp/. Modern Android blocks execution from /sdcard/. SocketSweep handles this automatically.
If SocketSweep saved you from the nightmare of MTP, consider supporting its development:
SocketSweep is released under the GNU General Public License v3.0. See the LICENSE file for more details.
Built by Vishnu Srivatsava. Inspired by the architecture of scrcpy. Currently looking for Backend / Systems Engineering roles. Feel free to reach out on LinkedIn or via email.


