Eine Bibliothek für den Zugriff auf USB-Massenspeichergeräte (USB-Sticks, externe Festplatten, Kartenleser) über die Android USB Host API. Derzeit werden der SCSI-Befehlssatz und das FAT32-Dateisystem unterstützt.
Die Bibliothek kann wie folgt in Ihr Projekt eingebunden werden:
implementation 'me.jahnen.libaums:core:0.10.0'
Wenn Sie das HTTP- oder das Speicheranbietermodul benötigen:
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)
}
Ihre App muss zur Laufzeit die Erlaubnis des Benutzers einholen, um mit dem Gerät kommunizieren zu können. Von einem UsbMassStorageDevice
können Sie dazu das zugrunde liegende android.usb.UsbDevice
abrufen.
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);
Weitere Informationen zu Berechtigungen finden Sie in der Android-Dokumentation: 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 ();
Wenn Sie relativ häufig (meistens unter Android 9.0 Pie) die folgende Fehlermeldung erhalten:
java.io.IOException: Could not write to device, result == -1 errno 0 null
oder etwas Ähnliches, vielleicht möchten Sie das libusb-Modul ausprobieren. Dabei wird anstelle der Android-USB-Host-API die libusb-Bibliothek für die Low-Level-Kommunikation mit dem USB-Massenspeichergerät verwendet.
siehe Diskussionen: #209 #237 #242
Beachten Sie , dass libusb unter LGPL lizenziert ist, was sich von der Lizenz unterscheidet, unter der dieses Projekt lizenziert ist! Dies kann einige Nachteile oder zusätzlichen Aufwand für Closed-Source-Anwendungen mit sich bringen, siehe hier: https://xebia.com/blog/the-lgpl-on-android/
Normalerweise haben Apps von Drittanbietern keinen Zugriff auf die Dateien auf einem Massenspeichergerät, wenn das Android-System tatsächlich gemountet wird (dies wird normalerweise auf neueren Geräten unterstützt, im Jahr 2014 gab es dafür keine Unterstützung). Das Gerät oder diese App integriert diese Bibliothek selbst . Um dieses Problem zu lösen, gibt es zwei zusätzliche Module, die den Zugriff auf andere Apps ermöglichen. Einer nutzt die Storage Access Framework-Funktion von Android (API-Level >= 19) und der andere richtet einen HTTP-Server ein, um beispielsweise das Herunterladen oder Streamen von Videos oder Bildern zu ermöglichen.
libaums unterstützt derzeit zwei verschiedene HTTP-Serverbibliotheken.
Sie können einen Server ganz einfach einrichten, Sie müssen sich nur für eine HTTP-Server-Implementierung entscheiden. Wenn Sie keine besonderen Anforderungen haben, können Sie sich einfach für eines entscheiden, es sollte keinen großen Unterschied machen.
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()
Die von Ihnen bereitgestellte Datei kann entweder eine tatsächliche Datei oder ein Verzeichnis sein:
Wenn Sie auf diese Dateien zugreifen möchten, während Ihre App im Hintergrund läuft, sollten Sie einen Dienst dafür implementieren. Ein Beispiel ist im httpserver
-Modul verfügbar. Sie können es verwenden, sollten es jedoch in eine Unterklasse umwandeln oder eine eigene erstellen, um es an Ihre Bedürfnisse anzupassen.
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 )
}
Weitere Einzelheiten hierzu finden Sie in der Beispiel-App.
Um mehr über diesen Besuch zu erfahren, besuchen Sie: https://developer.android.com/guide/topics/providers/document-provider.html
Um dieses Modul in Ihre App zu integrieren, müssen Sie lediglich die Definition in Ihre AndroidManifest.xml einfügen.
< 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 >
Danach können Apps, die das Storage Access Framework verwenden, auf die Dateien des USB-Massenspeichergeräts zugreifen.
app/
finden Sie eine Beispielanwendung, die die Bibliothek verwendet.UsbFile.setLength(long)
ein. Andernfalls muss die ClusterChain für jeden Schreibaufruf erhöht werden. Das ist sehr ineffizient.FileSystem.getChunkSize()
Bytes als Puffergröße, da dies mit den Blockgrößen übereinstimmt, die Laufwerke verwenden. Alles andere ist höchstwahrscheinlich auch ein Leistungsabfall.UsbFileStreamFactory
. Die Bibliothek wurde von Herrn Jahnen im Rahmen seiner Bachelorarbeit im Jahr 2014 entwickelt. Sie ist ein Unterthema des Forschungsthemas „Sicherer Kopierschutz für mobile Apps“ von Herrn Kannengießer. Das vollständige Dissertationsdokument kann hier heruntergeladen werden.
Libaums – Bibliothek für den Zugriff auf USB-Massenspeichergeräte
Lizenz: Apache 2.0 (Einzelheiten finden Sie in der Datei „license.txt“).
Autor: Magnus Jahnen, [email protected] Berater: Nils Kannengießer, nils.kannengiesser bei tum.de
Betreuer: Prof. Uwe Baumgarten, baumgaru bei in.tum.de
Technische Universität München (TUM)
Lehrstuhl/Fachgebiet für Betriebssysteme
www.os.in.tum.de