<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Vishnu Srivatsava</title>
    <description>The latest articles on Forem by Vishnu Srivatsava (@vishnusrivatsava).</description>
    <link>https://forem.com/vishnusrivatsava</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F966145%2F13677c17-26f3-496a-8e2a-95633b7108aa.jpg</url>
      <title>Forem: Vishnu Srivatsava</title>
      <link>https://forem.com/vishnusrivatsava</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/vishnusrivatsava"/>
    <language>en</language>
    <item>
      <title>I got tired of Android MTP hanging, so I bypassed it with a C++ daemon and Rust.</title>
      <dc:creator>Vishnu Srivatsava</dc:creator>
      <pubDate>Thu, 14 May 2026 07:22:59 +0000</pubDate>
      <link>https://forem.com/vishnusrivatsava/i-got-tired-of-android-mtp-hanging-so-i-bypassed-it-with-a-c-daemon-and-rust-3e82</link>
      <guid>https://forem.com/vishnusrivatsava/i-got-tired-of-android-mtp-hanging-so-i-bypassed-it-with-a-c-daemon-and-rust-3e82</guid>
      <description>&lt;p&gt;If you've ever plugged a 256GB Android phone into a Mac and tried to browse files, you know the pain. Android File Transfer hangs. OpenMTP crashes. MacDroid takes five minutes to list &lt;code&gt;/DCIM&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The root cause is MTP. Media Transfer Protocol is a synchronous, single-threaded protocol from the USB-IF spec, designed when phones had 4GB of storage and 200 photos. It was never built for a device with 400,000 files across nested directory trees. Every single folder listing is a blocking round-trip over USB, and the protocol has no concept of batch reads or parallel traversal.&lt;/p&gt;

&lt;p&gt;I got tired of it, so I built something that bypasses MTP entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;What if I could run native code &lt;em&gt;directly on the phone's filesystem&lt;/em&gt;, perform a POSIX-level recursive traversal (the same way &lt;code&gt;du&lt;/code&gt; or &lt;code&gt;find&lt;/code&gt; works on Linux), and pipe the results back to my Mac over a raw TCP socket?&lt;/p&gt;

&lt;p&gt;No MTP. No MediaStore queries. No Scoped Storage restrictions. Just &lt;code&gt;opendir()&lt;/code&gt;, &lt;code&gt;readdir()&lt;/code&gt;, &lt;code&gt;lstat()&lt;/code&gt;, streamed over a socket.&lt;/p&gt;

&lt;p&gt;That's SocketSweep&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/VishnuSrivatsava" rel="noopener noreferrer"&gt;
        VishnuSrivatsava
      &lt;/a&gt; / &lt;a href="https://github.com/VishnuSrivatsava/SocketSweep" rel="noopener noreferrer"&gt;
        SocketSweep
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A high-performance Android storage analyzer that bypasses MTP using a native C++ daemon, a Rust TCP bridge, and a React Treemap UI.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/VishnuSrivatsava/SocketSweep/assets/socket_sweep_logo.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FVishnuSrivatsava%2FSocketSweep%2FHEAD%2Fassets%2Fsocket_sweep_logo.png" alt="SocketSweep Logo" width="200"&gt;&lt;/a&gt;
  &lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;SocketSweep&lt;/h1&gt;
&lt;/div&gt;
  &lt;p&gt;&lt;strong&gt;A high-performance Android storage analyzer built to completely bypass the agonizingly slow USB MTP.&lt;/strong&gt;&lt;/p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/08cef40a9105b6526ca22088bc514fbfdbc9aac1ddbf8d4e6c750e3a88a44dca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d626c75652e737667"&gt;&lt;img src="https://camo.githubusercontent.com/08cef40a9105b6526ca22088bc514fbfdbc9aac1ddbf8d4e6c750e3a88a44dca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d626c75652e737667" alt="License: MIT"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/e5dc27433928762cccfc6e3760565c53ba75d7933aca1b3e82c04417a34f5cf4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f54617572692d76322d4646433133312e7376673f6c6f676f3d7461757269266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/e5dc27433928762cccfc6e3760565c53ba75d7933aca1b3e82c04417a34f5cf4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f54617572692d76322d4646433133312e7376673f6c6f676f3d7461757269266c6f676f436f6c6f723d7768697465" alt="Tauri v2"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/7244b9d38f3e2c0c78e13b643e397a3b553b29dbef9d1ee5307d3143148260c8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f52656163742d31392d3631444146422e7376673f6c6f676f3d7265616374266c6f676f436f6c6f723d626c61636b"&gt;&lt;img src="https://camo.githubusercontent.com/7244b9d38f3e2c0c78e13b643e397a3b553b29dbef9d1ee5307d3143148260c8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f52656163742d31392d3631444146422e7376673f6c6f676f3d7265616374266c6f676f436f6c6f723d626c61636b" alt="React 19"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/941d27166889fcacb536bac30e19a9c8c77aeff6854c186f95b85284293dde72/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f432532422532422d31372d3030353939432e7376673f6c6f676f3d63253242253242266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/941d27166889fcacb536bac30e19a9c8c77aeff6854c186f95b85284293dde72/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f432532422532422d31372d3030353939432e7376673f6c6f676f3d63253242253242266c6f676f436f6c6f723d7768697465" alt="C++17"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/80b204965f942b04425d3102a51aa47776be722275709cdba4ddf2e8e028b0b7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f527573742d312e37302b2d3030303030302e7376673f6c6f676f3d72757374266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/80b204965f942b04425d3102a51aa47776be722275709cdba4ddf2e8e028b0b7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f527573742d312e37302b2d3030303030302e7376673f6c6f676f3d72757374266c6f676f436f6c6f723d7768697465" alt="Rust"&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;p&gt;By pushing a custom C++ daemon directly to your Android device via ADB and communicating over a local TCP tunnel, SocketSweep achieves near-instantaneous filesystem traversals and deletions. If you have ever waited minutes just to see the contents of your Android's &lt;code&gt;/sdcard&lt;/code&gt; directory over a USB cable, SocketSweep is the ultimate, blazing-fast alternative.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📥 Downloads&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/VishnuSrivatsava/SocketSweep/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;Download SocketSweep v1.0.0 for macOS&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/VishnuSrivatsava/SocketSweep/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;📱 MacOS (.dmg)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href=""&gt;🪟 Windows (.exe)&lt;/a&gt; &lt;em&gt;(Coming Soon)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=""&gt;🐧 Linux (.AppImage)&lt;/a&gt; &lt;em&gt;(Coming Soon)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🏗 System Architecture&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;SocketSweep operates across a three-layer stack: &lt;strong&gt;The Glass&lt;/strong&gt; (Frontend), &lt;strong&gt;The Bridge&lt;/strong&gt; (Rust Backend), and &lt;strong&gt;The Engine&lt;/strong&gt; (C++ Android Daemon).&lt;/p&gt;

  &lt;div class="js-render-enrichment-target"&gt;
    &lt;div class="render-plaintext-hidden"&gt;
      &lt;pre&gt;flowchart TB
    subgraph Host["Host Desktop"]
        UI["React + Recharts&amp;lt;br&amp;gt;Interactive Dashboard"]
        Bridge["Rust / Tauri Backend&amp;lt;br&amp;gt;Command Orchestrator"]
        UI &amp;lt;--&amp;gt; Bridge
    end
    subgraph Transport["ADB Protocol"]
        Tunnel["ADB Port Forwarding&amp;lt;br&amp;gt;TCP:5050 -&amp;gt; TCP:5050"]
    end

    subgraph Device["Android Device"]
        Daemon["C++17 Daemon&amp;lt;br&amp;gt;Headless Socket Server"]
        Storage[("POSIX Filesystem&amp;lt;br&amp;gt;/sdcard")]
        
        Daemon &amp;lt;--&amp;gt; Storage
    end&lt;/pre&gt;…&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/VishnuSrivatsava/SocketSweep" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;Three layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────┐
│  React + Recharts (Treemap Visualization)   │  ← The Glass
├─────────────────────────────────────────────┤
│  Rust / Tauri (ADB orchestration, TCP I/O)  │  ← The Bridge  
├──────────── adb forward tcp:5050 ───────────┤
│  C++17 Daemon (POSIX filesystem walker)     │  ← The Engine
│  Running on Android via adb shell           │
└─────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Engine: A Headless C++ Daemon
&lt;/h3&gt;

&lt;p&gt;I wrote a ~350-line C++ program, cross-compiled for &lt;code&gt;aarch64-linux-android&lt;/code&gt; using the NDK's Clang toolchain (API level 31). The build script finds your local NDK installation, picks the right host tag, and spits out a statically-linked binary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$CC&lt;/span&gt; &lt;span class="nt"&gt;-std&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;c++17 &lt;span class="nt"&gt;-O2&lt;/span&gt; &lt;span class="nt"&gt;-Wall&lt;/span&gt; &lt;span class="nt"&gt;-Wextra&lt;/span&gt; &lt;span class="nt"&gt;-Wpedantic&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-fno-exceptions&lt;/span&gt; &lt;span class="nt"&gt;-fno-rtti&lt;/span&gt; &lt;span class="nt"&gt;-DNDEBUG&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-static-libstdc&lt;/span&gt;++ &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; daemon daemon.cpp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No exceptions, no RTTI, statically linked against libc++. The output is about 1.1MB.&lt;/p&gt;

&lt;p&gt;The daemon binds to &lt;code&gt;127.0.0.1:5050&lt;/code&gt; on the device and speaks a dead-simple text protocol:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;PING\n&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="err"&gt;←&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"pong"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;SCAN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/sdcard\n&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;←&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"scan_time_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"tree"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;DELETE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/path\n&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;←&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Deleted 42 items"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;SHUTDOWN\n&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;←&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"shutting down"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One command per TCP connection. The daemon accepts, reads one line, does the work, writes the full JSON response, closes the socket, and loops back to &lt;code&gt;accept()&lt;/code&gt;. No HTTP. No framing. No content-length headers. Just newline-delimited commands and EOF-delimited responses.&lt;/p&gt;

&lt;p&gt;The scanner itself is a straightforward recursive walk using &lt;code&gt;opendir()&lt;/code&gt; / &lt;code&gt;readdir()&lt;/code&gt; / &lt;code&gt;lstat()&lt;/code&gt;. It builds the entire file tree as an in-memory struct, sorts children by size (largest first), then serializes the whole thing as a single JSON blob through a 64KB write buffer to avoid hammering &lt;code&gt;send()&lt;/code&gt; with tiny writes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;FileNode&lt;/span&gt; &lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;ScanStats&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="kt"&gt;DIR&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;opendir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_str&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;dirent&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;ent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;readdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// skip . and .., skip symlinks&lt;/span&gt;
        &lt;span class="n"&gt;FileNode&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ent&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;d_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;closedir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// sort children descending by size&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things worth noting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Symlinks are skipped both at the &lt;code&gt;dirent&lt;/code&gt; level (&lt;code&gt;DT_LNK&lt;/code&gt; check) and at the &lt;code&gt;lstat&lt;/code&gt; level, because Android's &lt;code&gt;/sdcard&lt;/code&gt; is a FUSE mount with symlinks pointing into &lt;code&gt;/storage/emulated/0&lt;/code&gt;, and following them would cause infinite loops.&lt;/li&gt;
&lt;li&gt;Depth is capped at 64 to prevent stack overflow on pathological directory structures.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why &lt;code&gt;shell&lt;/code&gt; UID Is the Cheat Code
&lt;/h3&gt;

&lt;p&gt;The critical insight is &lt;em&gt;how&lt;/em&gt; the daemon gets filesystem access. When you push a binary to &lt;code&gt;/data/local/tmp&lt;/code&gt; and run it via &lt;code&gt;adb shell&lt;/code&gt;, it executes under the &lt;code&gt;shell&lt;/code&gt; user (UID 2000). On Android, this user has read access to &lt;code&gt;/sdcard&lt;/code&gt; — it completely sidesteps the Scoped Storage restrictions that would block a normal Android app from reading arbitrary files without user-granted URI permissions.&lt;/p&gt;

&lt;p&gt;To handle Android 11+'s additional &lt;code&gt;MANAGE_EXTERNAL_STORAGE&lt;/code&gt; restriction, the Rust bridge runs this before starting the daemon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb shell appops &lt;span class="nb"&gt;set &lt;/span&gt;com.android.shell MANAGE_EXTERNAL_STORAGE allow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Bridge: Rust + Tauri
&lt;/h3&gt;

&lt;p&gt;The desktop side is a Tauri v2 app. Rust handles the full daemon lifecycle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Kill any zombie daemon from a previous session (&lt;code&gt;pkill -f socketsweep_daemon&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Push the binary to the device (&lt;code&gt;adb push&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chmod +x&lt;/code&gt; it&lt;/li&gt;
&lt;li&gt;Launch it with &lt;code&gt;nohup&lt;/code&gt; in the background&lt;/li&gt;
&lt;li&gt;Set up the TCP tunnel (&lt;code&gt;adb forward tcp:5050 tcp:5050&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Ping-retry loop: attempt to connect every 150ms, up to 15 times, until the daemon responds&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The TCP communication is simple. Rust opens a connection to &lt;code&gt;127.0.0.1:5050&lt;/code&gt; (which ADB transparently tunnels to the device), writes a command, and calls &lt;code&gt;read_to_end()&lt;/code&gt; to consume the entire response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;daemon_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpStream&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;connect_timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;DAEMON_ADDR&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;TCP_CONNECT_TIMEOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.set_read_timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TCP_READ_TIMEOUT&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{cmd}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;response_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with_capacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.read_to_end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;response_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_utf8_lossy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;response_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response is passed as a raw JSON string directly to the React frontend through Tauri's IPC. No intermediate parsing on the Rust side.&lt;/p&gt;

&lt;p&gt;On shutdown, the bridge sends &lt;code&gt;SHUTDOWN&lt;/code&gt; to the daemon, removes the ADB port forward, and deletes the binary from the device. No trace left.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bug That Took the Longest
&lt;/h2&gt;

&lt;p&gt;TCP is a stream protocol. When the daemon was walking a large filesystem and blasting back a multi-megabyte JSON response, there was no guarantee that a single &lt;code&gt;read()&lt;/code&gt; call on the Rust side would return the complete payload. Early versions would read a partial buffer, try to hand a truncated JSON string like &lt;code&gt;{"name":"vid_&lt;/code&gt; to the frontend, and everything would break.&lt;/p&gt;

&lt;p&gt;The fix was &lt;code&gt;read_to_end()&lt;/code&gt; — it keeps reading until the daemon closes its end of the socket (EOF). This works cleanly because of the one-command-per-connection protocol: the daemon processes the command, writes the full response, and the implicit &lt;code&gt;close(client_fd)&lt;/code&gt; in the C++ code signals EOF to the Rust reader. No content-length framing needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Frontend
&lt;/h2&gt;

&lt;p&gt;Once the JSON tree arrives in React, I render it as an interactive Recharts Treemap. You can click into directories to zoom, see size proportions visually, and hit a "Nuke" button to delete files directly. The delete command goes back through the same path: React → Tauri IPC → Rust TCP → C++ daemon → &lt;code&gt;std::filesystem::remove_all&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F09khrme7smc0z8lg7vc1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F09khrme7smc0z8lg7vc1.png" alt=" " width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero-Dependency Distribution
&lt;/h2&gt;

&lt;p&gt;The final &lt;code&gt;.dmg&lt;/code&gt; bundles &lt;em&gt;everything&lt;/em&gt; — including the &lt;code&gt;adb&lt;/code&gt; binary itself as a Tauri resource. Users don't need Android platform-tools, Node, Rust, or the NDK installed. The Rust code resolves the bundled binary paths at runtime through Tauri's &lt;code&gt;AppHandle&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_bundled_binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;tauri&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AppHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;resource_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.resource_dir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resource_dir&lt;/span&gt;&lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bin"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bundled binary '{}' not found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every &lt;code&gt;Command::new()&lt;/code&gt; call in the codebase points to the bundled binary path. No &lt;code&gt;$PATH&lt;/code&gt; lookups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;The code is up on GitHub. If you want to poke at the cross-compilation setup, the socket protocol, or just see what's eating your phone's storage:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;→ &lt;a href="https://github.com/VishnuSrivatsava/SocketSweep" rel="noopener noreferrer"&gt;github.com/VishnuSrivatsava/SocketSweep&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Things I'm still thinking about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thread-pooling the C++ walker for 512GB+ devices where single-threaded traversal gets slow&lt;/li&gt;
&lt;li&gt;Stripping the binary with &lt;code&gt;aarch64-linux-android-strip&lt;/code&gt; to get it under 500KB&lt;/li&gt;
&lt;li&gt;Better error recovery if the USB cable gets yanked mid-scan&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've solved the USB-disconnect-mid-scan problem cleanly, or have thoughts on thread-pooling a recursive POSIX walker, I'd genuinely like to know.&lt;/p&gt;

</description>
      <category>android</category>
      <category>cpp</category>
      <category>rust</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
