magnusja / libaums

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

USB communication stops when app goes to background #278

Closed Derpalus closed 3 years ago

Derpalus commented 3 years ago

This is the scenario: The purpose of the USB part of the app is to read and upload a number of large files from a USB-connected SD card reader. As it is expected that this will be a long-running operation this functionality is implemented in a foreground service (with wake lock). The UsbMassStorageDevice and UsbManager are declared in a singleton that should survive as long as any part of the app does.

This works perfectly fine as long as the app is in the foreground. But as soon as the app goes into the background (either by being switched out or due to the screen turning off) the USB communication immediately starts failing. Note that the foreground service is alive and well.

While in foreground:

D/ClusterChain: Finished init of a cluster chain D/ClusterChain: Init a cluster chain, reading from FAT D/ClusterChain: Finished init of a cluster chain D/ClusterChain: Init a cluster chain, reading from FAT D/ClusterChain: Finished init of a cluster chain D/ClusterChain: Init a cluster chain, reading from FAT D/ClusterChain: Finished init of a cluster chain D/ClusterChain: Init a cluster chain, reading from FAT D/ClusterChain: Finished init of a cluster chain D/ClusterChain: Init a cluster chain, reading from FAT D/ClusterChain: Finished init of a cluster chain D/ClusterChain: Init a cluster chain, reading from FAT D/ClusterChain: Finished init of a cluster chain D/ClusterChain: Init a cluster chain, reading from FAT D/ClusterChain: Finished init of a cluster chain D/ClusterChain: Init a cluster chain, reading from FAT D/ClusterChain: Finished init of a cluster chain D/ClusterChain: Init a cluster chain, reading from FAT D/ClusterChain: Finished init of a cluster chain

Immediately after screen turned off:

I/.app: Background concurrent copying GC freed 201981(6833KB) AllocSpace objects, 19(428KB) LOS objects, 49% free, 7124KB/13MB, paused 125us total 150.953ms E/native_libusbcom: jint Java_me_jahnen_libaums_libusbcommunication_LibusbCommunication_nativeBulkTransfer(JNIEnv , jobject, jlong, jint, jbyteArray, jint, jint, jint):73 libusb_bulk_transfer returned -7 E/ScsiBlockDevice: Error transferring command; errno 0 null D/ScsiBlockDevice: Reset bulk-only mass storage W/ScsiBlockDevice: sending bulk only mass storage request D/ScsiBlockDevice: Trying to clear halt on both endpoints W/native_libusbcom: jint Java_me_jahnen_libaums_libusbcommunication_LibusbCommunication_nativeClearHalt(JNIEnv , jobject, jlong, jint):98 libusb reset D/ContentValues: libusb clearFeatureHalt returned kotlin.Unit W/native_libusbcom: jint Java_me_jahnen_libaums_libusbcommunication_LibusbCommunication_nativeClearHalt(JNIEnv , jobject, jlong, jint):98 libusb reset D/ContentValues: libusb clearFeatureHalt returned kotlin.Unit E/native_libusbcom: jint Java_me_jahnen_libaums_libusbcommunication_LibusbCommunication_nativeBulkTransfer(JNIEnv , jobject, jlong, jint, jbyteArray, jint, jint, jint):73 libusb_bulk_transfer returned -7 E/ScsiBlockDevice: Error transferring command; errno 0 null D/ScsiBlockDevice: Reset bulk-only mass storage W/ScsiBlockDevice: sending bulk only mass storage request D/ScsiBlockDevice: Trying to clear halt on both endpoints W/native_libusbcom: jint Java_me_jahnen_libaums_libusbcommunication_LibusbCommunication_nativeClearHalt(JNIEnv , jobject, jlong, jint):98 libusb reset D/ContentValues: libusb clearFeatureHalt returned kotlin.Unit W/native_libusbcom: jint Java_me_jahnen_libaums_libusbcommunication_LibusbCommunication_nativeClearHalt(JNIEnv , jobject, jlong, jint):98 libusb reset D/ContentValues: libusb clearFeatureHalt returned kotlin.Unit E/native_libusbcom: jint Java_me_jahnen_libaums_libusbcommunication_LibusbCommunication_nativeBulkTransfer(JNIEnv , jobject, jlong, jint, jbyteArray, jint, jint, jint):73 libusb_bulk_transfer returned -1 E/ScsiBlockDevice: Error transferring command; errno 0 null D/ScsiBlockDevice: Trying to reset the device W/native_libusbcom: jint Java_me_jahnen_libaums_libusbcommunication_LibusbCommunication_nativeReset(JNIEnv , jobject, jlong):92 libusb reset D/ContentValues: libusb reset returned kotlin.Unit E/native_libusbcom: jint Java_me_jahnen_libaums_libusbcommunication_LibusbCommunication_nativeBulkTransfer(JNIEnv *, jobject, jlong, jint, jbyteArray, jint, jint, jint):73 libusb_bulk_transfer returned -9 E/ScsiBlockDevice: Error transferring command; errno 0 null D/ScsiBlockDevice: Reset bulk-only mass storage W/ScsiBlockDevice: sending bulk only mass storage request

This is using the libusb variant, but it looks similar using the Android USB Host API.

Could it be Android USB permissions that gets revoked when the app goes into the background? Or is it as simple as me messing something up?

This is on an Android 10 device (Google Pixel 2) using libaums 0.7.7.

This does NOT happen on an Android 9 device (Samsung Galaxy Tab).

magnusja commented 3 years ago

Hey there,

thanks for the detailed report. So tbh I have never really tried transferring data in the background. But I assume it works. Maybe @Depau can help out? I think his app https://play.google.com/store/apps/details?id=eu.depau.etchdroid&hl=en&gl=US is transferring data in the background as well? (Which might also be worth trying for you)

Derpalus commented 3 years ago

I edited the first post just as you answered, so maybe you didn't see that this apparently doesn't happen in Android 9. I'm wondering if it's some heightened security (or just a bug) in Android 10 that is messing with the USB communication.

depau commented 3 years ago

Hi, yes EtchDroid works using a ForegroundService too, the app actually doesn't write anything in foreground at all, it only runs in the background. I usually recommend users to keep the screen on but I haven't seen any major issues with the screen off.

The code is here, don't look at it for too long or your eyes will start to burn :) https://github.com/EtchDroid/EtchDroid/tree/develop/app/src/main/java/eu/depau/etchdroid/services

I just grab a wakelock, here's the code responsible for it: https://github.com/EtchDroid/EtchDroid/blob/develop/app/src/main/java/eu/depau/etchdroid/services/UsbWriteService.kt#L207-L229 (you need to request the wakelock permission as well).

I also use the LibusbCommunication.

Derpalus commented 3 years ago

Ok I have found what was wrong and created a new branch on my fork with the fixes. The issue in this particular case was that a read was failing occasionally which wasn't handled gracefully by the current implementation (it just killed and tried to restart the device which would never be able to re-establish a stable communication). My branch also fixes an issue with SD card readers with more than one card slot, which in the current implementation will never work. I also suspect it will fix a lot of other strange issues due to more robust error handling and recovery.

The branch is called requestsense. How should we go on to integrate it in the current library?

magnusja commented 3 years ago

Hey there,

thanks a lot. Usually what you do on github then is creating a Pull Request: https://github.com/magnusja/libaums/compare/develop...Derpalus:requestsense?expand=1

Then we can merge it into this repo.

Derpalus commented 3 years ago

Ok, Pull Request created!