You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
USB file transfer (phone to RC): why it fails 30% of the time, and a SAF fix to test
Following a conversation with @spwoodcock about DroneTM's biggest mobile blocker: getting captured imagery off the DJI RC controller onto the pilot's phone over USB-C works only about 30% of the time. Sharing my findings and a small reference app so it does not get lost and someone can build on it.
The likely root cause
The DJI RC exposes its storage over MTP (Media Transfer Protocol). MTP does not mount as a filesystem on modern Android (scoped storage, API 29+). The older app (naxa-developers/dronetm-mobile) reaches the controller by guessing filesystem mount paths (/mnt/usb, /storage/usb, ...) and returning file:// URIs, as if it were a flash drive. Those paths do not exist for an MTP device on most phones, so it finds nothing. It only works on the few OEM ROMs that still surface USB as a mount, which is why the old code hardcodes specific phone models in device_filter.xml and requests MANAGE_EXTERNAL_STORAGE. That is the 30%.
So it is mostly a software issue, not missing drivers and not something that needs the NDK.
The fix
Talk to the device at the layer MTP actually lives at, the Storage Access Framework: content://, never file://.
Detect the device on plug-in (UsbManager + USB_DEVICE_ATTACHED, USB host mode).
Open it as a document tree (ACTION_OPEN_DOCUMENT_TREE + takePersistableUriPermission).
Copy files into a folder the user picks, streamed through ContentResolver, subfolders preserved.
No NDK, no MTP drivers, no MANAGE_EXTERNAL_STORAGE.
Reference app
I built a small standalone Android app that does it this way, as a proof of the mechanism:
It also shows a diagnostics panel (USB host support, the attached device, its interface class, whether MTP, and SAF access), which turns the invisible failure into a checklist.
The source device has to be unlocked and in File-transfer (MTP) mode, or MTP serves nothing. When it is not, the stock Android file picker itself shows "Can't load content at the moment" with the device still listed. That failure is in Android's own MTP provider, below any app, so no app-level code fixes it. This alone probably explains some of the randomness pilots see.
What is proven, and what still needs testing
Proven on my hardware (Pixel 7 Pro host, over an OTG adapter): detection, the diagnostics, and SAF read and copy, end to end, with both a USB mass-storage drive and a phone in MTP mode.
Not yet tested, and where help is needed:
An actual DJI RC2 controller.
Multiple source and host devices, including older and low-spec phones from different regions, since USB host support and MTP behaviour vary a lot by device.
If you have an RC2 or a range of phones, the APK above installs and runs standalone. What reads out in the diagnostics panel (host support yes/no, interface class) on a device where it fails would be very useful to capture here.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
USB file transfer (phone to RC): why it fails 30% of the time, and a SAF fix to test
Following a conversation with @spwoodcock about DroneTM's biggest mobile blocker: getting captured imagery off the DJI RC controller onto the pilot's phone over USB-C works only about 30% of the time. Sharing my findings and a small reference app so it does not get lost and someone can build on it.
The likely root cause
The DJI RC exposes its storage over MTP (Media Transfer Protocol). MTP does not mount as a filesystem on modern Android (scoped storage, API 29+). The older app (naxa-developers/dronetm-mobile) reaches the controller by guessing filesystem mount paths (
/mnt/usb,/storage/usb, ...) and returningfile://URIs, as if it were a flash drive. Those paths do not exist for an MTP device on most phones, so it finds nothing. It only works on the few OEM ROMs that still surface USB as a mount, which is why the old code hardcodes specific phone models indevice_filter.xmland requestsMANAGE_EXTERNAL_STORAGE. That is the 30%.So it is mostly a software issue, not missing drivers and not something that needs the NDK.
The fix
Talk to the device at the layer MTP actually lives at, the Storage Access Framework:
content://, neverfile://.UsbManager+USB_DEVICE_ATTACHED, USB host mode).ACTION_OPEN_DOCUMENT_TREE+takePersistableUriPermission).ContentResolver, subfolders preserved.No NDK, no MTP drivers, no
MANAGE_EXTERNAL_STORAGE.Reference app
I built a small standalone Android app that does it this way, as a proof of the mechanism:
It also shows a diagnostics panel (USB host support, the attached device, its interface class, whether MTP, and SAF access), which turns the invisible failure into a checklist.
Recordings
Recording 1 (MTP): copying off a second phone in File-transfer (MTP) mode over an OTG adapter. This is the same path the DJI RC uses.
https://github.com/user-attachments/assets/14b2dc2b-d69e-4a93-9606-f6ea6a0d00d9
Recording 2 (mass storage): the same app with a plain USB drive, showing the diagnostics reading it as mass storage vs MTP, and the full copy flow.
https://github.com/user-attachments/assets/44f55dfa-59ca-4eda-94d2-69c47cc91429
A field finding worth flagging
The source device has to be unlocked and in File-transfer (MTP) mode, or MTP serves nothing. When it is not, the stock Android file picker itself shows "Can't load content at the moment" with the device still listed. That failure is in Android's own MTP provider, below any app, so no app-level code fixes it. This alone probably explains some of the randomness pilots see.
What is proven, and what still needs testing
Proven on my hardware (Pixel 7 Pro host, over an OTG adapter): detection, the diagnostics, and SAF read and copy, end to end, with both a USB mass-storage drive and a phone in MTP mode.
Not yet tested, and where help is needed:
If you have an RC2 or a range of phones, the APK above installs and runs standalone. What reads out in the diagnostics panel (host support yes/no, interface class) on a device where it fails would be very useful to capture here.
All reactions