Uma biblioteca para acessar dispositivos de armazenamento em massa USB (pen drives, HDDs externos, leitores de cartão) usando a API Android USB Host. Atualmente ele suporta o conjunto de comandos SCSI e o sistema de arquivos FAT32.
A biblioteca pode ser incluída em seu projeto assim:
implementation 'me.jahnen.libaums:core:0.10.0'
Se você precisar do módulo HTTP ou do provedor de armazenamento:
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)
}
Seu aplicativo precisa obter permissão do usuário em tempo de execução para poder comunicar o dispositivo. A partir de um UsbMassStorageDevice
você pode obter o android.usb.UsbDevice
subjacente para fazer isso.
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 obter mais informações sobre permissões, consulte a documentação do 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 ();
Se você receber o seguinte erro com bastante frequência (principalmente no Android 9.0 Pie):
java.io.IOException: Could not write to device, result == -1 errno 0 null
ou algo semelhante, você pode tentar o módulo libusb. Isso usa, em vez da API de host USB do Android, a biblioteca libusb para comunicação de baixo nível com o dispositivo de armazenamento em massa USB.
veja as discussões: #209 #237 #242
Observe que o libusb está licenciado sob LGPL, que é diferente da licença sob a qual este projeto está licenciado! Isso pode trazer algumas desvantagens ou trabalho extra para aplicativos de código fechado, veja aqui: https://xebia.com/blog/the-lgpl-on-android/
Normalmente, aplicativos de terceiros não têm acesso aos arquivos em um dispositivo de armazenamento em massa se o sistema Android for montado (isso geralmente é compatível com dispositivos mais recentes, em 2014 não havia suporte para isso) o dispositivo ou este aplicativo integra esta biblioteca em si . Para resolver este problema existem dois módulos adicionais para fornecer acesso a outro aplicativo. Um usa o recurso Storage Access Framework do Android (nível de API> = 19) e o outro ativa um servidor HTTP para permitir o download ou streaming de vídeos ou imagens, por exemplo.
libaums atualmente suporta duas bibliotecas de servidores HTTP diferentes.
Você pode ativar um servidor com bastante facilidade, basta decidir por uma implementação de servidor HTTP. Se você não tem requisitos especiais, pode simplesmente optar por um, não deve fazer muita diferença.
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()
O arquivo que você fornece pode ser um arquivo real ou um diretório:
Se você quiser acessar esses arquivos quando seu aplicativo estiver em segundo plano, você deve implementar um serviço para isso. Há um exemplo disponível no módulo httpserver
. Você pode usá-lo, mas deve subclassificá-lo ou criar o seu próprio para adaptá-lo às suas necessidades.
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 o aplicativo de exemplo para obter detalhes adicionais sobre isso.
Para saber mais sobre esta visita: https://developer.android.com/guide/topics/providers/document-provider.html
Para integrar este módulo em seu aplicativo, a única coisa que você precisa fazer é adicionar a definição em seu 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 >
Depois disso, os aplicativos que usam o Storage Access Framework poderão acessar os arquivos do dispositivo de armazenamento em massa USB.
app/
você pode encontrar um exemplo de aplicativo usando a biblioteca.UsbFile.setLength(long)
. Caso contrário, o ClusterChain deverá ser aumentado para cada chamada escrita. Isso é muito ineficiente.FileSystem.getChunkSize()
como tamanho do buffer, porque isso se alinha com os tamanhos de bloco que as unidades estão usando. Todo o resto também é provavelmente uma diminuição no desempenho.UsbFileStreamFactory
. A biblioteca foi desenvolvida pelo Sr. Jahnen como parte de sua tese de bacharelado em 2014. É um subtópico do tópico de pesquisa "Proteção segura contra cópia para aplicativos móveis" do Sr. O documento completo da tese pode ser baixado aqui.
Libaums - Biblioteca para acessar dispositivos de armazenamento em massa USB
Licença: Apache 2.0 (veja License.txt para detalhes)
Autor: Magnus Jahnen, [email protected] Conselheiro: Nils Kannengießer, nils.kannengiesser em tum.de
Supervisor: Prof. Uwe Baumgarten, baumgaru em in.tum.de
Universidade Técnica de Munique (TUM)
Lehrstuhl/Fachgebiet für Betriebssysteme
www.os.in.tum.de