使用 Android USB Host API 存取 USB 大容量儲存裝置(隨身碟、外部 HDD、讀卡機)的程式庫。目前它支援SCSI指令集和FAT32檔案系統。
該庫可以像這樣包含到您的專案中:
implementation 'me.jahnen.libaums:core:0.10.0'
如果您需要 HTTP 或儲存提供者模組:
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)
}
您的應用程式需要在運行時獲得用戶的許可才能與設備進行通訊。從UsbMassStorageDevice
中,您可以取得底層android.usb.UsbDevice
來執行此操作。
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);
有關權限的更多信息,請查看 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 ();
如果您經常遇到以下錯誤(主要是在 Android 9.0 Pie 下):
java.io.IOException: Could not write to device, result == -1 errno 0 null
或類似的東西,您可能想嘗試 libusb 模組。它使用 libusb 庫而不是 Android USB 主機 API 來與 USB 大容量儲存裝置進行低階通訊。
請參閱討論:#209 #237 #242
請注意,libusb 是根據 LGPL 獲得許可的,這與該專案獲得許可的許可不同!對於閉源應用程式來說,這可能會帶來一些缺點或額外的工作,請參閱此處:https://xebia.com/blog/the-lgpl-on-android/
通常,如果Android 系統確實安裝了設備或此應用程式本身整合了此程式庫(這通常在較新的設備上支持,早在2014 年還不支援),則第三方應用程式將無法存取大容量儲存設備上的文件。為了解決這個問題,有兩個額外的模組可以提供對其他應用程式的存取。使用 Android 的儲存存取框架功能(API 等級 >= 19),另一則啟動 HTTP 伺服器以允許下載或串流視訊或映像等。
libaums 目前支援兩種不同的 HTTP 伺服器庫。
您可以非常輕鬆地啟動伺服器,只需決定 HTTP 伺服器實作即可。如果沒有特殊要求的話,買一個就可以了,應該不會有太大差別。
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()
您提供的文件可以是實際文件或目錄:
如果您希望在應用程式處於背景時能夠存取這些文件,則應該為此實作一項服務。 httpserver
模組中有一個範例。您可以使用它,但應該對其進行子類化或創建您自己的以適應您的需求。
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 )
}
有關更多詳細信息,請參閱範例應用程式。
要了解有關此訪問的更多資訊:https://developer.android.com/guide/topics/providers/document-provider.html
要將此模組整合到您的應用程式中,您唯一要做的就是在 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 >
之後,使用儲存存取框架的應用程式將能夠存取 USB 大容量儲存裝置的檔案。
app/
目錄中,您可以找到使用該庫的範例應用程式。UsbFile.setLength(long)
設定長度。否則每次調用寫入時都必須增加 ClusterChain。這是非常低效的。FileSystem.getChunkSize()
位元組作為緩衝區大小,因為這與磁碟機正在使用的區塊大小一致。其他一切也很可能會導致效能下降。UsbFileStreamFactory
。 該庫由 Jahnen 先生在 2014 年作為其學士論文的一部分開發。完整的論文檔案可以在這裡下載。
Libaums - 存取 USB 大容量儲存裝置的庫
許可證:Apache 2.0(詳細資訊請參閱license.txt)
作者:Magnus Jahnen,[email protected] 顧問:Nils Kannengießer,nils.kannengiesser at tum.de
指導教授:Uwe Baumgarten 教授,in.tum.de 的 baumgaru
慕尼黑工業大學 (TUM)
Lehrstuhl/Fachgebiet für Betriebssysteme
www.os.in.tum.de