使用 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 年作为其学士论文的一部分开发。它是 Kannengießer 先生研究主题“移动应用程序的安全复制保护”的子主题。完整的论文文件可以在这里下载。
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