Porterage

Version 0.1.0 · Android to macOS, over cable

A Mac app that copies files to and from an Android phone. It speaks the phone's transfer protocol directly, which is why the numbers on this page are not the ones you are used to.

Not notarised by Apple yet, so the first launch needs a right-click → Open. One phone model has been tested properly. What else it does not do

Folder listing

0.56s

345 files · 3.2 s the usual way

Small files

53ms

each, and it stays there

Photo library

79s

324 photos · 2 GiB

Requires

14+

macOS · universal · under 3 MB

Three ways the other tools fail

Each one was reproduced on a Redmi 9T against a MacBook Air M1, and each is the reason a corresponding part of Porterage is built the way it is.

  1. 01

    It stops answering after a couple of hundred files

    The phone raises an interrupt event for every object written. A client that never reads that queue slows down as the queue fills, and then MTP stops responding entirely — a USB reset does not recover it. You have to physically unplug the cable.

    Queue drained53 msper file
    Not drained1 100 msper file

    Same phone, same 400 files. Undrained, it wedged for good at roughly 211 objects.

  2. 02

    Opening a folder takes longer than it should

    The common library marks every Android device as unable to use MTP's bulk listing command, and offers no way to override it — so it asks the phone about each file in turn. Porterage implements the protocol itself and asks once per property instead.

    Porterage0.56 s345 files
    One request per file3.2 s345 files

    Both bars share one scale. At ten thousand photos the gap is about 95 seconds against six.

  3. 03

    Two files that differ only in case destroy each other

    The phone's storage ignores capitalisation; the protocol does not. So Report.txt and report.txt both appear in a listing while sharing one file underneath, and writing the second silently overwrites the first. Nothing warns you. Porterage compares names the way the storage actually does, and refuses the write.

    Silently overwritten elsewhere

    Refused here, with the conflicting name shown

Every tile in the grid gets a picture

MTP's thumbnail command is not a thumbnail service. It hands back whatever the file's own EXIF header already holds — which, for a screenshot or a downloaded image, is nothing. So there are three routes, tried in that order, and the grid fills instead of showing blanks.

  1. Route 1

    Ask the phone

    The embedded thumbnail, sent straight off the phone. Camera photos almost always carry one.

    14 ms each · 57 of 60 camera photos · 300 photos in 4.1 s

  2. Route 2

    Read the first 64 KiB

    When the phone returns nothing, pull the header down and look for the embedded JPEG here.

    9 ms each · found one in 17 of the 20 tried

  3. Route 3

    Fetch it and scale it on the Mac

    The whole file, resized on this end. Screenshots and downloaded images land here.

    About 15% of a real phone · files over 40 MiB skipped

The whole pass runs after every visible tile already holds something, so scrolling is never waiting on a picture.

What it does when things go wrong

Interruption is the normal case with a cable. It is designed for, not apologised for.

Cable pulled mid-copy

Resumes at the exact byte

The phone reports precisely how much of the file it already holds, so a resumed copy starts at the real boundary instead of a guess. Verified in both directions by comparing SHA-256 of the finished file against the original.

Resumed upload 19.4 MiB/s · resumed download 28.8 MiB/s

Screen locks while copying

Keeps going at full speed

Android blocks a computer from starting a session on a locked phone, but it does not touch one already running. Eleven consecutive 512 MiB reads finished with the screen locked the whole time, at the same speed as unlocked, with no errors.

Locked throughput 29.1–29.5 MiB/s · 0 failures

Phone is nearly full

Refuses before it starts

The phone itself will happily accept a file far larger than its remaining space and fail minutes later. Its reported free space, though, is exact to the byte — so Porterage checks first and tells you how much is missing.

64 MiB written moved free space by 64.1 MiB, and back on delete

Nothing shows up

Says which of five things is wrong

Locked screen, wrong USB mode, charge-only cable, another app holding the port, or a wedged connection needing a replug. Each has a different signature on the wire, and each gets its own sentence instead of “busy or not connected”.

Five states, five instructions

Drag a row straight into the Finder

The file is fetched when you let go, not when you pick it up. Start the drag on a large photo and it moves with the cursor immediately; the copy happens once the drop lands somewhere.

Drop files the other way, copy whole folders in either direction, and watch the free space on the phone move as it happens.

Copied out
29 MiB/s
Copied in
15 MiB/s
Deleted
25 ms each

Also in the window

  • Rows or a grid of thumbnails
  • Rename, and delete behind a confirmation
  • New folder, select all
  • Search that ignores accents
  • Sort by name, size or date — folders lead either way
  • Space bar to preview a photo, arrow keys to step through

Conditions of measurement

One phone, one Mac, one cable. Your hardware will differ — these are the conditions the numbers above were taken under, not a promise about yours.

Phone
Redmi 9T, MIUI, Android 11
Computer
MacBook Air M1, macOS 26.5.1
Sustained
39 consecutive 512 MiB round trips, about 20 GiB, no errors and no drift
Large files
4 402 341 478 bytes transferred and read back correctly past the 4 GiB mark
First listing
About 17 s after plugging in, while the phone indexes ~34 000 objects. Every listing after that, 0.17 s

What it does not do

Stated here because finding out later is worse.

  • Not signed by Apple yet. macOS will warn on first launch until a Developer ID certificate is in place. Right-click the app and choose Open to get past it.
  • No Wi-Fi, no cloud, no account. A cable, and nothing installed on the phone.
  • Dates survive one direction only. Files copied to the Mac keep their original date; the phone stamps its own clock on anything copied to it, and refuses to be told otherwise.
  • Memory cards are untested. The phone used for development has no card slot in use, so that path has never been exercised.
  • 0.1.0 is early. One phone model has been tested properly. If yours behaves differently, the connection report in the app is designed to say why.