Android USB 호스트 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 모듈을 사용해 볼 수도 있습니다. 이는 USB 대용량 저장 장치와의 낮은 수준 통신을 위해 Android USB 호스트 API 대신 libusb 라이브러리를 사용합니다.
토론 참조: #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
도 참조하세요. 이 라이브러리는 Mr. Jahnen이 2014년 학사 논문의 일부로 개발했습니다. 이는 Mr. Kannengießer의 연구 주제 "모바일 앱을 위한 보안 복사 방지"의 하위 주제입니다. 전체 논문 문서는 여기에서 다운로드할 수 있습니다.
Libaums - USB 대용량 저장 장치에 액세스하기 위한 라이브러리
라이센스: Apache 2.0(자세한 내용은 License.txt 참조)
작성자: Magnus Jahnen, [email protected] 고문: Nils Kannengeßer, nils.kannengiesser(tum.de)
감독자: Uwe Baumgarten 교수, in.tum.de의 baumgaru
뮌헨 공과대학(TUM)
Lehrstuhl/Fachgebiet für Betriebssysteme
www.os.in.tum.de