@@ -47,6 +47,7 @@ public final class DashcamStorageManager {
4747
4848 private static final String USB_RECORDS_DIR_NAME = "dashcam" ;
4949 private static final String WRITE_PROBE_FILE_NAME = ".dashcam_write_probe" ;
50+ private static final File LEGACY_STORAGE_ROOT = new File ("/storage" );
5051
5152 public enum UsbState {
5253 NOT_CHECKED , // INTERNAL_ONLY mode: USB is deliberately ignored
@@ -192,6 +193,18 @@ private static final class UsbCandidate {
192193 */
193194 private static UsbProbe probeUsb (Context context ) {
194195 List <UsbCandidate > candidates = findUsbCandidates (context );
196+ if (candidates .isEmpty ()) {
197+ // AAOS 9 head units typically don't surface OTG/USB volumes via StorageManager:
198+ // the OS mounts them under /mnt/media_rw/... without registering them with the
199+ // MediaProvider, so getExternalFilesDirs() returns nothing usable. Fall back to a
200+ // direct /storage scan, which is how every dashcam-style automotive app has to
201+ // handle this in practice.
202+ candidates = findLegacyStorageCandidates ();
203+ if (!candidates .isEmpty ()) {
204+ Log .i (TAG , "StorageManager exposed no removable volumes; using /storage scan ("
205+ + candidates .size () + " candidate(s))" );
206+ }
207+ }
195208 if (candidates .isEmpty ()) {
196209 return new UsbProbe (UsbState .NO_MEDIUM , null );
197210 }
@@ -256,6 +269,27 @@ private static List<UsbCandidate> findUsbCandidates(Context context) {
256269 return new ArrayList <>(deduped .values ());
257270 }
258271
272+ /**
273+ * Last-resort enumeration: list every directory under /storage that isn't the emulated
274+ * primary volume or the "self" symlink. No StorageManager metadata, no isRemovable hints,
275+ * just whatever the user can mount and read. Suitability is decided by the write probe.
276+ */
277+ private static List <UsbCandidate > findLegacyStorageCandidates () {
278+ List <UsbCandidate > candidates = new ArrayList <>();
279+ File [] children = LEGACY_STORAGE_ROOT .listFiles ();
280+ if (children == null ) {
281+ return candidates ;
282+ }
283+ for (File child : children ) {
284+ if (child == null || !child .isDirectory ()) continue ;
285+ String name = child .getName ();
286+ if (name .isEmpty () || "emulated" .equals (name ) || "self" .equals (name )) continue ;
287+ if (!child .canRead ()) continue ;
288+ candidates .add (new UsbCandidate (child , name , looksLikeUsb (child , name )));
289+ }
290+ return candidates ;
291+ }
292+
259293 private static List <UsbCandidate > prioritizeUsbCandidates (List <UsbCandidate > candidates ) {
260294 List <UsbCandidate > usbLike = new ArrayList <>();
261295 List <UsbCandidate > genericRemovable = new ArrayList <>();
0 commit comments