Library / SDK
magnusja/libaums avatar
magnusja/libaums

libaums for Android USB Mass Storage Access

Open source library to access USB Mass Storage devices on Android without rooting your device

1,364 stars298 forksKotlinApache-2.0

At a glance

What is it?
libaums lets Android applications read and write USB mass storage devices through the Android USB Host API without a documented root-based workflow. Its documented scope is SCSI and FAT32, with optional HTTP, Storage Access Framework, and libusb-based modules.
Who is it for?
Android developers should adopt libaums when an application must read or write files on a USB drive that Android does not expose through its normal mounted-storage interface. It is a poor fit for unsupported file systems, workflows requiring partitions beyond the documented first partition, or applications unable to obtain runtime USB permission.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 75 days ago.
What is it written in?
Mainly Kotlin, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why libaums Targets Direct Android USB Drive Access

libaums addresses a specific Android storage gap. An application may need to read or write a USB pen drive, external hard disk, or compatible card reader even when Android does not mount it as conventional shared storage. The library uses the Android USB Host API, and the README does not document root access as a requirement for its normal communication path.

The intended users are Android developers building direct file access, local media delivery, kiosk software, or device-specific integrations. Those applications must still request permission from the user before communicating with the attached device. libaums does not bypass Android's security model. The application obtains the underlying `android.usb.UsbDevice` from a `UsbMassStorageDevice` and passes it to `usbManager.requestPermission`.

The documented support boundary is narrow. libaums supports the SCSI command set and the FAT32 file system. The README does not claim support for exFAT, NTFS, ext4, or other formats, so a drive's filesystem label alone is not enough to establish compatibility. It also does not promise uniform behavior across every phone, tablet, Android release, or OEM USB implementation.

This makes libaums different from Android APIs that read files exposed through mounted storage. Its object model and streams address files directly through a mass storage device. That capability is useful when Android does not mount the medium, but it also makes device permission, partition layout, and connection lifecycle application responsibilities rather than transparent platform behavior.

How libaums Connects Partitions, FAT32, and File Streams

Discovery starts with `UsbMassStorageDevice.getMassStorageDevices(this)`, which returns compatible mass storage devices from the Android environment. The README requires every selected device to be initialized with `init()` before interaction. The application then accesses a partition's `FileSystem` and reads capacity, occupied space, free space, and chunk size.

The basic example explicitly selects `device.partitions[0]`. This is a meaningful boundary: the documented example uses the first partition, while the README does not describe automatic partition selection by type, label, or intended data location. A multi-partition drive may therefore need application logic beyond the basic example.

libaums represents root directories, directories, and files through `UsbFile`. A developer can obtain `fileSystem.rootDirectory`, call `listFiles()`, inspect metadata, and create children with `createDirectory` or `createFile`. Standard stream classes connect that object model to input and output operations through `UsbFileInputStream` and `UsbFileOutputStream`.

For buffering, `UsbFileStreamFactory.createBufferedInputStream(file, currentFs)` and `createBufferedOutputStream(file, currentFs)` bind a buffer to the same file-system context. The README does not document buffer sizes, transfer throughput, or comparative performance. It would therefore be unsupported to claim that either stream type is faster.

Lifecycle management is explicit. Applications must call `UsbMassStorageDevice.close()` when finished. The README does not enumerate recovery behavior after abrupt device removal or describe how partial writes are reconciled, so those cases require direct validation in the consuming application.

Installing libaums and Performing the First FAT32 Read

The README includes the core library in an Android Gradle project at version `0.10.0`. This artifact provides device discovery, initialization, FAT32 access, and the documented `UsbFile` API.

ruby
implementation 'me.jahnen.libaums:core:0.10.0'

An application must obtain USB permission before it communicates with a device. The documented Kotlin example creates a broadcast `PendingIntent` for `ACTION_USB_PERMISSION` and requests permission for `device.usDevice`.

kotlin
val permissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), 0);
usbManager.requestPermission(device.usDevice, permissionIntent);

Permission is asynchronous. The README refers readers to Android's USB host documentation for more information, but it does not provide a complete activity callback example. An application must retain enough context to continue with the selected device after permission is granted.

Discovery returns the devices visible to the current `Context` or `Activity`. This call does not replace the runtime permission request.

kotlin
val devices = UsbMassStorageDevice.getMassStorageDevices(this /* Context or Activity */)

After selecting and initializing a permitted device, the application can inspect its first partition. The following abbreviated sequence follows the README's ordering.

kotlin
for (device in devices) {
    device.init()
    val currentFs = device.partitions[0].fileSystem
    Log.d(TAG, "Capacity: " + currentFs.capacity)
    Log.d(TAG, "Free Space: " + currentFs.freeSpace)
}

A production application should not initialize every discovered device blindly. The README example is introductory and does not specify selection, duplicate-device handling, or user-interface policy. The first real integration check should confirm that only the intended device reaches initialization and permission handling.

Writing and Reading a File through UsbFile

File operations begin at `fileSystem.rootDirectory`. The documented Kotlin example creates a directory named `foo`, creates `bar.txt` inside it, writes the bytes for `hello`, and closes the output stream.

kotlin
val root = currentFs.rootDirectory
val newDir = root.createDirectory("foo")
val file = newDir.createFile("bar.txt")

val os = UsbFileOutputStream(file)
os.write("hello".toByteArray())
os.close()

Closing the stream is an explicit part of the documented example. libaums does not present ordinary Android content-provider write semantics here. Operations travel through the USB mass storage device and its FAT32 file-system implementation.

Reading uses `UsbFileInputStream`. The README allocates a buffer from the file system's `chunkSize` and calls `read(buffer)` once. That call alone does not establish that the entire file was received, and the example does not include partial-read handling. Applications that require a complete file must process the stream until the intended end condition.

kotlin
val ins = UsbFileInputStream(file)
val buffer = ByteArray(currentFs.chunkSize)
ins.read(buffer)

The stream factory can create buffered variants for the same file and `FileSystem`.

java
OutputStream os = UsbFileStreamFactory.createBufferedOutputStream(file, currentFs);
InputStream is = UsbFileStreamFactory.createBufferedInputStream(file, currentFs);

The README does not document cancellation, checksums, atomic rename, resume support, or behavior after an I/O error. A device-removal test is therefore necessary if the application copies data continuously. Also note that the README's Kotlin listing example checks `isDirectory` before logging `length`, while the Java example checks `!file.isDirectory()`. That inconsistency makes the examples illustrative rather than a precise metadata contract.

Choosing the Android USB API or libusbcommunication Module

The standard libaums communication route uses Android's USB Host API. The repository also contains `libusbcommunication`, which uses libusb for low-level communication. The README presents this alternative mainly in troubleshooting rather than as the default dependency.

One documented concern is a recurring Android 9.0 Pie write failure with the following exception text:

text
java.io.IOException: Could not write to device, result == -1 errno 0 null

The README says that applications encountering this or a similar problem might try the libusb module. It does not say that libusb solves every storage failure, improves speed, or removes the need for Android permission handling. The supported claim is narrower: applications have an alternative low-level communication implementation to evaluate when the normal USB API produces recurring write errors.

There is also a licensing difference. The main project uses Apache-2.0, while the README states that libusb is licensed under LGPL and warns that this can create extra work or drawbacks for closed source applications. That statement is not legal advice, but it is a concrete dependency boundary for release engineering and distribution review.

The Android USB Host API path is the documented default. The libusb path changes both the communication implementation and the license considerations. Selection should follow reproduced device failures and project distribution requirements, not an unsupported assumption about throughput.

Optional HTTP and StorageProvider Exposure Modules

libaums offers two optional ways to expose files when other Android applications cannot access the USB drive through Android's mounted-storage mechanism. One module uses Android's Storage Access Framework, which the README says requires API level 19 or later. The other runs an HTTP server for downloads or streaming of files such as videos and images.

The README lists both optional modules at version `0.6.2`.

ruby
implementation 'me.jahnen.libaums:httpserver:0.6.2'
implementation 'me.jahnen.libaums:storageprovider:0.6.2'

The HTTP module supports NanoHTTPD and AsyncHttpServer. Both documented constructors accept port `8000`.

java
HttpServer server = AsyncHttpServer(8000); // port 8000
// or
HttpServer server = NanoHttpdServer(8000); // port 8000

The README says either implementation is reasonable without special requirements, but it does not document authentication, TLS, network binding, access control, or exposure to untrusted clients. Port `8000` should therefore not be interpreted as a security boundary.

The README does not supply the `storageprovider` manifest, XML, intent-filter, or URI setup. Applications cannot infer a complete Storage Access Framework integration from the dependency declaration alone. Direct same-process access needs `core`, while these modules serve distinct file-exposure requirements. Their code and version histories should remain separate from the core upgrade decision.

libaums Release Versions and Upgrade Cost

The repository is not archived, and its last push was on 2026-08-09. The listed releases are older. `v0.10.0` was released on 2023-03-11, `v0.9.4` on 2023-03-10, and `v0.9.3` on 2023-03-10. These dates describe the repository records available to this review; they do not establish a release schedule.

The README's installation example uses `me.jahnen.libaums:core:0.10.0`, while `httpserver` and `storageprovider` are shown at `0.6.2`. The release list also identifies component versions for several releases. Module versions should therefore be managed independently rather than inferred from the repository tag name.

The project license is Apache-2.0. Selecting `libusbcommunication` introduces the LGPL distinction documented in the README, which may require a separate review of packaging and distribution. This is a technical compliance consideration, not legal advice.

Upgrade cost depends on Android lifecycle handling, USB permission callbacks, partition assumptions, stream management, and actual device behavior. The README does not provide a migration guide from `0.9.x` to `0.10.0`. A concrete first check is to build against `core:0.10.0`, then verify discovery, permission denial and approval, initialization, a small write, a complete read, and `UsbMassStorageDevice.close()` on the intended Android devices.

Editorial conclusion

Android developers should adopt libaums when an application must read or write files on a USB drive that Android does not expose through its normal mounted-storage interface. It is a poor fit for unsupported file systems, workflows requiring partitions beyond the documented first partition, or applications unable to obtain runtime USB permission. Review the current module versions and API before upgrading, because the README lists `core` and its optional modules at different versions. Verify permission handling, FAT32 compatibility, device removal behavior, and the licensing of the communication path on representative Android hardware.

Frequently asked questions

Which devices and file systems does libaums support?

libaums supports USB mass storage devices such as pen drives, external hard disks, and card readers. Its documented protocol and file-system support is limited to SCSI and FAT32.

How do I add libaums to an Android project?

The README adds the core library with `implementation 'me.jahnen.libaums:core:0.10.0'`. The HTTP server and storage provider modules have separate documented dependencies.

Why does my Android app need USB permission to use libaums?

The application must obtain runtime permission from the user. It exposes the underlying `android.usb.UsbDevice` through `UsbMassStorageDevice` and passes that device to `usbManager.requestPermission`.

Does libaums access every partition on a USB drive?

The README's basic example explicitly uses only the first partition. It does not document automatic selection of later partitions by type or label.

What should an Android app do after finishing with a libaums device?

It should call `UsbMassStorageDevice.close()`. The application must also call `device.init()` before interacting with the device.

Can libaums use libusb instead of the Android USB Host API?

Yes, the repository includes `libusbcommunication`, which uses libusb for low-level communication. The README associates it with some Android USB write failures and warns that it uses LGPL rather than Apache-2.0.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. magnusja/libaums on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/magnusja-libaums.svg)](https://hysenlabs.com/projects/magnusja-libaums)

Was this useful?