magnusja / libaums

Open source library to access USB Mass Storage devices on Android without rooting your device
Apache License 2.0
1.27k stars 272 forks source link

Using Libaums just to measure free drive space and then close it #324

Closed gavingt closed 2 years ago

gavingt commented 2 years ago

Libaums has very fast methods for measuring free drive space and drive capacity, whereas Android 12 has crippled our ability to measure these through normal means. My goal is to copy files to the USB drive using the standard Android API (by getting a Uri from SAF), and then when I'm done I want to use Libaums to measure the drive space. But I'm running into a problem with this, as it seems that SAF won't fully release control of the USB drive. In fact, trying to init the drive using Libaums corrupts many of the files that were just copied to the USB drive. Is there some way to use Libaums side-by-side with the standard Android APIs?

Here's my method for measuring space:

fun getDriveInfoFromLibaums(): Boolean {
    val deviceListFromLibaums = UsbMassStorageDevice.getMassStorageDevices(appContext)
    if (deviceListFromLibaums.isNullOrEmpty()) return false

    var usbMassStorageDevice: UsbMassStorageDevice? = null
    try {
        usbMassStorageDevice = deviceListFromLibaums[0]
        val tempUsbDevice = usbMassStorageDevice.usbDevice
        usbMassStorageDevice.init()
        val currentFileSystem = usbMassStorageDevice.partitions[0].fileSystem

        prefs.lastRecordedDriveSize = currentFileSystem.capacity
        prefs.lastRecordedDriveFreeSpace = currentFileSystem.freeSpace
        usbMassStorageDevice.close()

        return true
    } catch (throwable: Throwable) {
        usbMassStorageDevice?.close()
        return false
    }
}
depau commented 2 years ago

The Android storage access framework will use the regular filesystem to write data, which means it will do some buffering and finish writing in the background.

If you want to take control of the USB drive with libaums you need to ensure the drive is "safely removed" by somehow invoking the "Eject" functionality that is found in the system USB notification. I don't know how you can do this programmatically.

gavingt commented 2 years ago

Thanks for the response. This certainly explains the problems I've been experiencing. I've found that I can go from Libaums to SAF with no problem, but going from SAF to Libaums results in corrupted files.

I've tried to look through the Libaums code to learn how it calculates drive space. It seems like the approach is similar to this except it's operating on custom classes and it's much faster in Android 12. Is there perhaps a way to use the Libaums approach within the standard Android APIs? Or do you know of anything other than ContentResolver.openAssetFileDescriptor() that can be used for that purpose?

depau commented 2 years ago

Is there perhaps a way to use the Libaums approach within the standard Android APIs?

No. Libaums directly accesses the USB drive by issuing SCSI commands directly, bypassing the Linux kernel's block device subsystem. Android does not, and it should never, since it is not in the condition of a regular unprivileged app that needs to work around limitations.

You should probably look for the source code of ContentResolver.openAssetFileDescriptor(), see what it does and see what changed in the latest release.

gavingt commented 2 years ago

Can you confirm that Libaums doesn't do any such buffering or writing in the background after calling usbMassStorageDevice.close()? I'm trying to determine whether it's safe to use Libaums at app start to measure drive space and then close it and use SAF after.

depau commented 2 years ago

It does not. However, yours doesn't look like a great idea: there could be pending writes before the user launches your app and you're likely to corrupt their filesystem.

How about tweaking the user experience so the user doesn't get bored while you're loading the disk space? Add a spinner and wait for the file descriptor in a background thread.