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 モジュールを試してみるとよいでしょう。これは、Android USB ホスト API の代わりに、USB 大容量ストレージ デバイスとの低レベル通信に libusb ライブラリを使用します。
ディスカッションを参照: #209 #237 #242
libusb は LGPL に基づいてライセンスされており、このプロジェクトがライセンスされているライセンスとは異なることに注意してください。これには、クローズド ソース アプリケーションの場合、いくつかの欠点や余分な作業が伴う可能性があります。こちらを参照してください: https://xebia.com/blog/the-lgpl-on-android/
Android システムがマウントする場合 (これは通常、新しいデバイスでサポートされていますが、2014 年当時はサポートされていませんでした)、デバイスまたはこのアプリがこのライブラリ自体を統合する場合、通常、サードパーティのアプリは大容量ストレージ デバイス上のファイルにアクセスできません。 。この問題を解決するには、他のアプリへのアクセスを提供する 2 つの追加モジュールがあります。 1 つは Android のストレージ アクセス フレームワーク機能 (API レベル >= 19) を使用し、もう 1 つは HTTP サーバーを起動して、たとえばビデオや画像のダウンロードまたはストリーミングを可能にします。
libaums は現在、2 つの異なる 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