GNirl is a native macOS menu bar app that provides reliable Android reverse tethering over USB. It wraps gnirehtet with automatic reconnection, health monitoring, and ADB keepalive — solving the stability issues that make raw gnirehtet unusable for long sessions.
gnirehtet works great for a few seconds, then silently dies. ADB connections go stale, the tunnel drops on idle, macOS power management kills the USB link, and the Java/Rust client crashes with no recovery. You're left restarting the process every 10 seconds.
GNirl fixes all of that.
- Menu bar app — lives in your status bar, zero windows needed
- Automatic reconnection — detects device disconnects, gnirehtet crashes, and ADB staleness within 5 seconds and reconnects automatically
- ADB keepalive — sends periodic pings to prevent USB idle sleep
- Sleep management — prevents macOS from sleeping while connected, auto-reconnects on wake
- Live log window — watch gnirehtet traffic in real time with copy/clear
- Colored status icons — green (connected), red (dropped), orange (connecting), gray (idle)
- Custom icon support — drop your own PNGs for each status state
- macOS 14+ (Sonoma or later)
- ADB —
brew install --cask android-platform-tools - gnirehtet —
brew install gnirehtet - Android device with USB debugging enabled
# Install dependencies
brew install --cask android-platform-tools
brew install gnirehtet
# Clone and build
git clone https://github.com/YOUR_USERNAME/GNirl.git
cd GNirl
./build.sh
# Open the app
open GNirl.appswift build -c release
cp .build/release/GNirl GNirl.app/Contents/MacOS/GNirl
open GNirl.app- Plug in your Android via USB (USB debugging must be enabled)
- Open GNirl from the menu bar
- Click your device from the dropdown list
- Done — the icon turns green and your phone has internet through your Mac
The first time you connect, GNirl automatically installs the gnirehtet APK on your device.
| Icon | Meaning |
|---|---|
| Green icon | Connected and working |
| Red icon | Dropped/error — will auto-reconnect |
| Orange icon | Connecting |
| Gray icon | Idle, waiting for device |
Click Open Log Window to see live gnirehtet output. Use Copy All to grab the full log, or Clear to reset it.
gnirehtet sets up a VPN/tunnel on your Android. If you unplug the phone (or quit GNirl) without stopping the tunnel, your phone can get stuck with the VPN active and lose Wi‑Fi/mobile data.
When you’re finished:
- In GNirl, disconnect/stop the current device so the VPN is torn down (go back to Idle / ensure the device shows as disconnected).
- Only then unplug the USB cable.
If your phone gets stuck:
- Reconnect the phone to USB and use GNirl to restore/stop the tunnel, or restart the phone’s network (toggle airplane mode / reboot) to clear the stale VPN state.
Drop PNG files into GNirl.app/Contents/Resources/ with these names:
| File | Shown when |
|---|---|
status_connected.png |
Connected |
status_connecting.png |
Connecting |
status_error.png |
Dropped/error |
status_idle.png |
Idle/disconnected |
Icons are automatically scaled to 20pt for the menu bar. Any resolution works.
Convert your PNG to .icns and place it at:
GNirl.app/Contents/Resources/AppIcon.icns
Or use the included conversion script:
./scripts/make-icon.sh your-image.pngGNirl is a thin Swift supervisor around gnirehtet:
- ADBWatcher polls
adb devices -levery 3 seconds to detect connected Android devices - GnirehtetService launches
gnirehtet runas a child process and monitors it:- Health check every 5 seconds (process alive + device still connected)
- ADB keepalive every 10 seconds (prevents USB idle sleep)
- Exponential backoff reconnect (2s → 4s → 8s → 30s max)
- IOPMAssertion to prevent system sleep while connected
- Graceful SIGINT shutdown on disconnect
- Make sure USB debugging is enabled on your phone
- Check
adb devicesin Terminal — if it showsunauthorized, tap "Allow" on the phone's prompt - Try a different USB cable (some are charge-only, no data)
- On the phone, set USB mode to File Transfer or Charging (not "USB tethering" — that's the opposite direction)
- The old Python script issue is gone — GNirl's health check and keepalive prevent this
- If it still happens, check the log window for gnirehtet errors
- Try
gnirehtet reinstall <serial>from the menu bar to refresh the APK
These are harmless gnirehtet noise. The Rust TCP stack spams them on every connection close. Traffic flows normally.
Right-click GNirl.app → Open → click Open in the dialog. This is a one-time macOS Gatekeeper bypass for ad-hoc signed apps.
- gnirehtet by Genymobile — the reverse tethering engine
- Built with Swift and SwiftUI
MIT