Una biblioteca para acceder a dispositivos de almacenamiento masivo USB (pendrives, discos duros externos, lectores de tarjetas) utilizando la API USB Host de Android. Actualmente admite el conjunto de comandos SCSI y el sistema de archivos FAT32.
La biblioteca se puede incluir en su proyecto de esta manera:
implementation 'me.jahnen.libaums:core:0.10.0'
Si necesita el módulo HTTP o de proveedor de almacenamiento:
implementation 'me.jahnen.libaums:httpserver:0.6.2'
implementation 'me.jahnen.libaums:storageprovider:0.6.2'
UsbMassStorageDevice [] devices = UsbMassStorageDevice . getMassStorageDevices ( this /* Context or Activity */ );
for ( UsbMassStorageDevice device : devices ) {
// before interacting with a device you need to call init()!
device . init ();
// Only uses the first partition on the device
FileSystem currentFs = device . getPartitions (). get ( 0 ). getFileSystem ();
Log . d ( TAG , "Capacity: " + currentFs . getCapacity ());
Log . d ( TAG , "Occupied Space: " + currentFs . getOccupiedSpace ());
Log . d ( TAG , "Free Space: " + currentFs . getFreeSpace ());
Log . d ( TAG , "Chunk size: " + currentFs . getChunkSize ());
}
val devices = UsbMassStorageDevice .getMassStorageDevices( this /* Context or Activity */ )
for (device in devices) {
// before interacting with a device you need to call init()!
device. init ()
// Only uses the first partition on the device
val currentFs = device.partitions[ 0 ].fileSystem
Log .d( TAG , " Capacity: " + currentFs.capacity)
Log .d( TAG , " Occupied Space: " + currentFs.occupiedSpace)
Log .d( TAG , " Free Space: " + currentFs.freeSpace)
Log .d( TAG , " Chunk size: " + currentFs.chunkSize)
}
Su aplicación necesita obtener permiso del usuario en tiempo de ejecución para poder comunicarse con el dispositivo. Desde un UsbMassStorageDevice
puede obtener el android.usb.UsbDevice
subyacente para hacerlo.
PendingIntent permissionIntent = PendingIntent . getBroadcast ( this , 0 , new Intent ( ACTION_USB_PERMISSION ), 0 );
usbManager . requestPermission ( device . getUsbDevice (), permissionIntent );
val permissionIntent = PendingIntent .getBroadcast( this , 0 , Intent ( ACTION_USB_PERMISSION ), 0 );
usbManager.requestPermission(device.usDevice, permissionIntent);
Para obtener más información sobre los permisos, consulte la documentación de Android: https://developer.android.com/guide/topics/connectivity/usb/host.html#permission-d
UsbFile root = currentFs . getRootDirectory ();
UsbFile [] files = root . listFiles ();
for ( UsbFile file : files ) {
Log . d ( TAG , file . getName ());
if (! file . isDirectory ()) {
Log . d ( TAG , file . getLength ());
}
}
UsbFile newDir = root . createDirectory ( "foo" );
UsbFile file = newDir . createFile ( "bar.txt" );
// write to a file
OutputStream os = new UsbFileOutputStream ( file );
os . write ( "hello" . getBytes ());
os . close ();
// read from a file
InputStream is = new UsbFileInputStream ( file );
byte [] buffer = new byte [ currentFs . getChunkSize ()];
is . read ( buffer );
val root = currentFs.rootDirectory
val files = root.listFiles()
for (file in files) {
Log .d( TAG , file.name)
if (file.isDirectory) {
Log .d( TAG , file.length)
}
}
val newDir = root.createDirectory( " foo " )
val file = newDir.createFile( " bar.txt " )
// write to a file
val os = UsbFileOutputStream (file)
os.write( " hello " .toByteArray())
os.close()
// read from a file
val ins = UsbFileInputStream (file)
val buffer = ByteArray (currentFs.chunkSize)
ins.read(buffer)
OutputStream os = UsbFileStreamFactory . createBufferedOutputStream ( file , currentFs );
InputStream is = UsbFileStreamFactory . createBufferedInputStream ( file , currentFs );
// Don't forget to call UsbMassStorageDevice.close() when you are finished
device . close ();
Si recibe el siguiente error con bastante frecuencia (principalmente en Android 9.0 Pie):
java.io.IOException: Could not write to device, result == -1 errno 0 null
o algo similar, quizás quieras probar el módulo libusb. Esto utiliza, en lugar de la API del host USB de Android, la biblioteca libusb para la comunicación de bajo nivel con el dispositivo de almacenamiento masivo USB.
ver discusiones: #209 #237 #242
Tenga en cuenta que libusb tiene licencia LGPL, que es diferente de la licencia bajo la cual este proyecto tiene licencia. Esto podría tener algunos inconvenientes o trabajo adicional para aplicaciones de código cerrado, consulte aquí: https://xebia.com/blog/the-lgpl-on-android/
Por lo general, las aplicaciones de terceros no tienen acceso a los archivos en un dispositivo de almacenamiento masivo si el sistema Android se monta (esto generalmente es compatible con dispositivos más nuevos, en 2014 no había soporte para eso). El dispositivo o esta aplicación integra esta biblioteca. . Para resolver este problema, hay dos módulos adicionales para brindar acceso a otra aplicación. Uno utiliza la función Storage Access Framework de Android (nivel de API >= 19) y el otro activa un servidor HTTP para permitir la descarga o transmisión de vídeos o imágenes, por ejemplo.
libaums actualmente admite dos bibliotecas de servidor HTTP diferentes.
Puede activar un servidor con bastante facilidad, sólo tiene que decidirse por una implementación de servidor HTTP. Si no tienes requisitos especiales, puedes optar por uno, no debería hacer mucha diferencia.
UsbFile file = ... // can be directory or file
HttpServer server = AsyncHttpServer ( 8000 ); // port 8000
// or
HttpServer server = NanoHttpdServer ( 8000 ); // port 8000
UsbFileHttpServer fileServer = new UsbFileHttpServer ( file , server );
fileServer . start ();
val file : UsbFile
// can be directory or file
val server = AsyncHttpServer ( 8000 ) // port 8000
// or
val server = NanoHttpdServer ( 8000 ) // port 8000
val fileServer = UsbFileHttpServer (file, server)
fileServer.start()
El archivo que proporcione puede ser un archivo real o un directorio:
Si desea poder acceder a estos archivos cuando su aplicación está en segundo plano, debe implementar un servicio para ello. Hay un ejemplo disponible en el módulo httpserver
. Puedes usarlo, pero debes subclasificarlo o crear el tuyo propio para adaptarlo a tus necesidades.
private UsbFileHttpServerService serverService ;
ServiceConnection serviceConnection = new ServiceConnection () {
@ Override
public void onServiceConnected ( ComponentName name , IBinder service ) {
Log . d ( TAG , "on service connected " + name );
UsbFileHttpServerService . ServiceBinder binder = ( UsbFileHttpServerService . ServiceBinder ) service ;
serverService = binder . getService ();
}
@ Override
public void onServiceDisconnected ( ComponentName name ) {
Log . d ( TAG , "on service disconnected " + name );
serverService = null ;
}
};
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
...
serviceIntent = new Intent ( this , UsbFileHttpServerService . class );
...
}
@ Override
protected void onStart () {
super . onStart ();
startService ( serviceIntent );
bindService ( serviceIntent , serviceConnection , Context . BIND_AUTO_CREATE );
}
private void startHttpServer ( final UsbFile file ) {
...
serverService . startServer ( file , new AsyncHttpServer ( 8000 ));
...
}
private var serverService : UsbFileHttpServerService ? = null
internal var serviceConnection : ServiceConnection = object : ServiceConnection () {
override fun onServiceConnected ( name : ComponentName , service : IBinder ) {
Log .d( TAG , " on service connected $name " )
val binder = service as UsbFileHttpServerService . ServiceBinder
serverService = binder.getService()
}
override fun onServiceDisconnected ( name : ComponentName ) {
Log .d( TAG , " on service disconnected $name " )
serverService = null
}
}
override protected fun onCreate ( savedInstanceState : Bundle ) {
serviceIntent = Intent ( this , UsbFileHttpServerService :: class .java)
}
override protected fun onStart () {
super .onStart()
startService(serviceIntent)
bindService(serviceIntent, serviceConnection, Context . BIND_AUTO_CREATE )
}
Consulte la aplicación de ejemplo para obtener detalles adicionales al respecto.
Para obtener más información sobre esta visita: https://developer.android.com/guide/topics/providers/document-provider.html
Para integrar este módulo en tu aplicación lo único que tienes que hacer es agregar la definición en tu AndroidManifest.xml.
< provider
android : name = " me.jahnen.libaums.storageprovider.UsbDocumentProvider "
android : authorities = " me.jahnen.libaums.storageprovider.documents "
android : exported = " true "
android : grantUriPermissions = " true "
android : permission = " android.permission.MANAGE_DOCUMENTS "
android : enabled = " @bool/isAtLeastKitKat " >
< intent-filter >
< action android : name = " android.content.action.DOCUMENTS_PROVIDER " />
</ intent-filter >
</ provider >
Después de eso, las aplicaciones que utilicen Storage Access Framework podrán acceder a los archivos del dispositivo de almacenamiento masivo USB.
app/
puede encontrar una aplicación de ejemplo que utiliza la biblioteca.UsbFile.setLength(long)
. De lo contrario, se debe aumentar ClusterChain para cada llamada de escritura. Esto es muy ineficiente.FileSystem.getChunkSize()
como tamaño del búfer, porque esto se alinea con los tamaños de bloque que utilizan las unidades. Todo lo demás probablemente también sea una disminución del rendimiento.UsbFileStreamFactory
. La biblioteca fue desarrollada por el Sr. Jahnen como parte de su tesis de licenciatura en 2014. Es un subtema del tema de investigación "Protección segura contra copia para aplicaciones móviles" del Sr. Kannengießer. El documento completo de tesis se puede descargar aquí.
Libaums - Biblioteca para acceder a dispositivos de almacenamiento masivo USB
Licencia: Apache 2.0 (consulte licencia.txt para obtener más detalles)
Autor: Magnus Jahnen, [email protected] Asesor: Nils Kannengießer, nils.kannengiesser en tum.de
Supervisor: Prof. Uwe Baumgarten, baumgaru en in.tum.de
Universidad Técnica de Múnich (TUM)
Lehrstuhl/Fachgebiet für Betriebssysteme
www.os.in.tum.de