USB アクセサリ モードでは、ユーザーが Android 搭載デバイス用に設計された USB ホスト ハードウェアを接続できます。アクセサリは、Android Accessory Development Kit ドキュメントに記載されている Android アクセサリ プロトコルに準拠している必要があります。これにより、USB ホストとして動作できない Android 搭載デバイスでも USB ハードウェアと通信できるようになります。Android 搭載デバイスを USB アクセサリ モードにすると、接続された Android USB アクセサリがホストとして動作し、USB バスに電源を供給して、接続されているデバイスを列挙します。Android 3.1(API レベル 12)が USB アクセサリ モードをサポートしているほか、幅広いデバイスのサポートを可能にするために、この機能は Android 2.3.4(API レベル 10)にも移植されています。
適切な USB アクセサリ API を選択する
USB アクセサリ API は Android 3.1 でプラットフォームに導入されましたが、Android 2.3.4 でも Google API アドオン ライブラリを使用して提供されています。こうした API は外部ライブラリを使用して移植されているため、USB アクセサリ モードをサポートするためにインポートできるパッケージは 2 つ存在します。サポートする Android 搭載デバイスに応じて、次のいずれかの方法の使用が必要となります。
com.android.future.usb
: Android 2.3.4 で USB アクセサリ モードをサポートするため、移植された USB アクセサリ API が Google API アドオン ライブラリに収録されており、この名前空間に含まれています。Android 3.1 でも、このアドオン ライブラリを使用して作成されたアプリをサポートするために、この名前空間内のクラスのインポートと呼び出しをサポートしています。このアドオン ライブラリはandroid.hardware.usb
アクセサリ API のシンラッパーであり、USB ホストモードはサポートしていません。USB アクセサリ モードに対応した幅広いデバイスをサポートする場合は、アドオン ライブラリを使用して、このパッケージをインポートします。すべての Android 2.3.4 デバイスに USB アクセサリ機能のサポートが求められているわけではない、という点に注意してください。この機能をサポートするかどうかは各デバイス メーカーが判断します。マニフェスト ファイルでの宣言が必要なのはこのためです。android.hardware.usb
: この名前空間には、Android 3.1 で USB アクセサリ モードをサポートするクラスが含まれています。このパッケージはフレームワーク API の一部として含まれているため、Android 3.1 はアドオン ライブラリを使用せずに USB アクセサリ モードをサポートしています。USB アクセサリ モードをサポートするハードウェアを持つ Android 3.1 以降のデバイスのみを対象とする場合(このことはマニフェスト ファイル内で宣言が可能です)は、このパッケージを使用します。
Google API アドオン ライブラリをインストールする
アドオンをインストールする場合は、SDK Manager で Google API Android API 10 パッケージをインストールすることによって、アドオンをインストールできます。アドオン ライブラリのインストールについて詳しくは、Google API アドオンのインストールについての説明をご覧ください。
API の概要
アドオン ライブラリはフレームワーク API のラッパーであるため、USB アクセサリ機能をサポートするクラスは類似しています。アドオン ライブラリを使用する場合でも、android.hardware.usb
のリファレンス ドキュメントを使用できます。
注: ただし、アドオン ライブラリとフレームワーク API との間には、わずかに使用方法の違いがあるため、ご注意ください。
USB アクセサリ API をサポートしているクラスを、次の表に示します。
クラス | 説明 |
---|---|
UsbManager |
接続された USB デバイスの列挙と通信が可能です。 |
UsbAccessory |
USB アクセサリを表します。また、識別情報にアクセスするためのメソッドを含んでいます。 |
アドオン ライブラリとプラットフォーム API の使用方法の違い
Google API アドオン ライブラリとプラットフォーム API の使用には、使用方法の違いが 2 つあります。
アドオン ライブラリを使用している場合は、次の方法で UsbManager
オブジェクトを取得する必要があります。
Kotlin
val manager = UsbManager.getInstance(this)
Java
UsbManager manager = UsbManager.getInstance(this);
アドオン ライブラリを使用していない場合は、次の方法で UsbManager
オブジェクトを取得する必要があります。
Kotlin
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
Java
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
接続されているアクセサリをインテント フィルタでフィルタする場合、アプリに渡されるインテント内に UsbAccessory
オブジェクトが含まれています。アドオン ライブラリを使用している場合は、次の方法で UsbAccessory
オブジェクトを取得する必要があります。
Kotlin
val accessory = UsbManager.getAccessory(intent)
Java
UsbAccessory accessory = UsbManager.getAccessory(intent);
アドオン ライブラリを使用していない場合は、次の方法で UsbAccessory
オブジェクトを取得する必要があります。
Kotlin
val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory
Java
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
Android マニフェストの要件
USB アクセサリ API を使用する前にアプリのマニフェスト ファイルに追加する必要がある項目を、次のリストに示します。こうした項目の宣言方法については、マニフェストとリソース ファイルの例をご覧ください。
- すべての Android 搭載デバイスが USB アクセサリ API をサポートしているわけではないため、アプリで
android.hardware.usb.accessory
機能を使用することを宣言する<uses-feature>
要素を含めます。 - アドオン ライブラリを使用している場合は、ライブラリの
com.android.future.usb.accessory
を指定した<uses-library>
要素を追加します。 - アドオン ライブラリを使用している場合はアプリの最小 SDK を API レベル 10 に設定します。または、
android.hardware.usb
パッケージを使用している場合は 12 に設定します。 -
USB アクセサリが接続されたときにアプリに通知されるようにする場合は、メイン アクティビティに
android.hardware.usb.action.USB_ACCESSORY_ATTACHED
インテント用の<intent-filter>
要素と<meta-data>
要素のペアを指定します。<meta-data>
要素で、検出するアクセサリについての識別情報を宣言する外部 XML リソース ファイルを指定します。この XML リソース ファイル内で、フィルタするアクセサリについての
<usb-accessory>
要素を宣言します。各<usb-accessory>
には次の属性を指定できます。manufacturer
model
version
リソース ファイルを
res/xml/
ディレクトリに保存します。リソース ファイル名(.xml 拡張子なし)は、<meta-data>
要素で指定した名前と同じにする必要があります。XML リソース ファイルの形式については、下記の例もご覧ください。
マニフェストとリソース ファイルの例
マニフェストと、対応するリソース ファイルの例を次に示します。
<manifest ...> <uses-feature android:name="android.hardware.usb.accessory" /> <uses-sdk android:minSdkVersion="<version>" /> ... <application> <uses-library android:name="com.android.future.usb.accessory" /> <activity ...> ... <intent-filter> <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" android:resource="@xml/accessory_filter" /> </activity> </application> </manifest>
この場合、次のリソース ファイルを res/xml/accessory_filter.xml
に保存し、対応するモデル、メーカー、およびバージョンのアクセサリがすべてフィルタされるように指定する必要があります。アクセサリは Android 搭載デバイスに次の属性を送信します。
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/> </resources>
アクセサリと連携する
ユーザーが USB アクセサリを Android 搭載デバイスに接続した際に、Android システムは、アプリがその接続されたアクセサリと関連しているかどうかを判断できます。関連している場合、デベロッパーは必要に応じてそのアクセサリとの通信を設定できます。そのためには、アプリで次の処理を行う必要があります。
- アクセサリの接続イベントをフィルタするインテント フィルタを使用するか、接続済みのアクセサリを列挙して必要なアクセサリを見つけることによって、接続されたアクセサリを検出します。
- アクセサリと通信する許可をユーザーに求めます(まだ取得していない場合)。
- 適切なインターフェースのエンドポイントでデータの読み取りと書き込みを行うことによって、アクセサリと通信します。
アクセサリを検出する
アプリでは、インテント フィルタを使用してユーザーがアクセサリを接続したときに通知されるようにするか、すでに接続されているアクセサリを列挙することによって、アクセサリを検出できます。アプリで必要なアクセサリを自動的に検出できるようにする場合は、インテント フィルタを使用すると便利です。接続済みのすべてのアクセサリの一覧の取得が必要な場合や、アプリでインテントのフィルタをしなかった場合は、接続されているアクセサリを列挙すると便利です。
インテント フィルタを使用する
アプリで特定の USB アクセサリを検出するために、インテント フィルタを指定して android.hardware.usb.action.USB_ACCESSORY_ATTACHED
インテントをフィルタできます。このインテント フィルタに加えて、メーカー、モデル、バージョンといった USB アクセサリのプロパティが記述されたリソース ファイルを指定する必要があります。ユーザーがアクセサリ フィルタに一致するアクセサリを接続すると、
インテント フィルタを宣言する方法の例を次に示します。
<activity ...> ... <intent-filter> <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" android:resource="@xml/accessory_filter" /> </activity>
関連している USB アクセサリを指定した、対応するリソース ファイルを宣言する方法を次の例に示します。
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" /> </resources>
アクティビティで、接続されたアクセサリを表す UsbAccessory
を、次のようにして(アドオン ライブラリを使用して)インテントから取得できます。
Kotlin
val accessory = UsbManager.getAccessory(intent)
Java
UsbAccessory accessory = UsbManager.getAccessory(intent);
プラットフォーム API を使用する場合は次のようになります。
Kotlin
val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory
Java
UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
アクセサリを列挙する
アプリが実行している間、アプリはシステムが識別済みのアクセサリを列挙できます。
getAccessoryList()
メソッドを使用して、接続されているすべての USB アクセサリの配列を取得します。
Kotlin
val manager = getSystemService(Context.USB_SERVICE) as UsbManager val accessoryList: Array<out UsbAccessory> = manager.accessoryList
Java
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); UsbAccessory[] accessoryList = manager.getAccessoryList();
注: 一度に 1 つの接続済みアクセサリのみがサポートされています。
アクセサリとの通信の権限を取得する
USB アクセサリと通信する前に、アプリがユーザーから権限を取得する必要があります。
注: アプリがインテント フィルタを使用してアクセサリを接続時に検出する場合、ユーザーがアプリにインテントの処理を許可すれば、アプリは自動的に権限を獲得します。そうでない場合は、アクセサリに接続する前に、アプリ内で明示的に権限をリクエストする必要があります。
権限の明示的なリクエストが必要になるのは、アプリがすでに接続済みのアクセサリを列挙してそのうちの 1 台と通信する場合などです。アクセサリと通信する前に、アクセサリへのアクセス権限があるかを確認する必要があります。そうしないと、ユーザーがアクセサリへのアクセスを拒否した場合にランタイム エラーが発生します。
明示的に権限を取得するには、まずブロードキャスト レシーバを作成します。このレシーバは、requestPermission()
の呼び出し時にブロードキャストを取得するインテントをリッスンします。requestPermission()
を呼び出すと、アクセサリへの接続の権限を求めるダイアログがユーザーに表示されます。ブロードキャスト レシーバの作成方法を次のサンプルコードに示します。
Kotlin
private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION" private val usbReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (ACTION_USB_PERMISSION == intent.action) { synchronized(this) { val accessory: UsbAccessory? = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { accessory?.apply { //call method to set up accessory communication } } else { Log.d(TAG, "permission denied for accessory $accessory") } } } } }
Java
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"; private final BroadcastReceiver usbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_USB_PERMISSION.equals(action)) { synchronized (this) { UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { if(accessory != null){ //call method to set up accessory communication } } else { Log.d(TAG, "permission denied for accessory " + accessory); } } } } };
ブロードキャスト レシーバを登録するには、アクティビティの onCreate()
メソッド内に以下を追加します。
Kotlin
private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION" ... val manager = getSystemService(Context.USB_SERVICE) as UsbManager ... permissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), 0) val filter = IntentFilter(ACTION_USB_PERMISSION) registerReceiver(usbReceiver, filter)
Java
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"; ... permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); registerReceiver(usbReceiver, filter);
ユーザーにアクセサリへの接続の権限を求めるダイアログを表示するには、次のように requestPermission()
メソッドを呼び出します。
Kotlin
lateinit var accessory: UsbAccessory ... usbManager.requestPermission(accessory, permissionIntent)
Java
UsbAccessory accessory; ... usbManager.requestPermission(accessory, permissionIntent);
ユーザーがダイアログに応答すると、ブロードキャスト レシーバは、回答を表すブール値である EXTRA_PERMISSION_GRANTED
エクストラを含んだインテントを受信します。アクセサリに接続する前に、このエクストラの値が true であるかを確認します。
アクセサリと通信する
UsbManager
を使用してファイル記述子を取得することでアクセサリと通信できます。ファイル記述子には、記述子に対するデータの読み取りや書き込みを行う入出力ストリームを設定できます。ストリームは、アクセサリの入出力用バルク エンドポイントを表します。メインの UI スレッドをブロックしないよう、デバイスとアクセサリとの間の通信は別のスレッドに設定する必要があります。通信するアクセサリを開く方法を次の例に示します。
Kotlin
private lateinit var accessory: UsbAccessory private var fileDescriptor: ParcelFileDescriptor? = null private var inputStream: FileInputStream? = null private var outputStream: FileOutputStream? = null ... private fun openAccessory() { Log.d(TAG, "openAccessory: $mAccessory") fileDescriptor = usbManager.openAccessory(accessory) fileDescriptor?.fileDescriptor?.also { fd -> inputStream = FileInputStream(fd) outputStream = FileOutputStream(fd) val thread = Thread(null, this, "AccessoryThread") thread.start() } }
Java
UsbAccessory accessory; ParcelFileDescriptor fileDescriptor; FileInputStream inputStream; FileOutputStream outputStream; ... private void openAccessory() { Log.d(TAG, "openAccessory: " + accessory); fileDescriptor = usbManager.openAccessory(accessory); if (fileDescriptor != null) { FileDescriptor fd = fileDescriptor.getFileDescriptor(); inputStream = new FileInputStream(fd); outputStream = new FileOutputStream(fd); Thread thread = new Thread(null, this, "AccessoryThread"); thread.start(); } }
スレッドの run()
メソッドで FileInputStream
または FileOutputStream
オブジェクトを使用することで、アクセサリへの読み取りや書き込みが可能です。FileInputStream
オブジェクトを使用してアクセサリからデータを読み取る場合は、使用するバッファに、USB パケットのデータを保存できる十分な容量があることを確認します。Android アクセサリ プロトコルでは最大 16,384 バイトのパケット バッファをサポートしているため、常にこのサイズでバッファを宣言すると処理を簡略化できます。
注: 下位レベルでは、USB Full-Speed アクセサリのパケットは 64 バイト、USB High-Speed アクセサリのパケットは 512 バイトです。Android アクセサリ プロトコルでは、処理を簡略化するために、どちらの速度でも複数のパケットを 1 つの論理パケットにバンドルします。
Android でのスレッドの使用について詳しくは、プロセスとスレッドについての説明をご覧ください。
アクセサリとの通信を終了する
アクセサリとの通信が終了したときや、アクセサリの接続が解除された場合は、close()
を呼び出すことで、開いたファイル記述子を閉じます。接続解除のイベントをリッスンするには、次のようにしてブロードキャスト レシーバを作成します。
Kotlin
var usbReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (UsbManager.ACTION_USB_ACCESSORY_DETACHED == intent.action) { val accessory: UsbAccessory? = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) accessory?.apply { // call your method that cleans up and closes communication with the accessory } } } }
Java
BroadcastReceiver usbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) { UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); if (accessory != null) { // call your method that cleans up and closes communication with the accessory } } } };
マニフェストではなくアプリ内でブロードキャスト レシーバを作成することにより、アプリが実行中にのみ接続解除のイベントを処理できるようになります。そうすることで、接続解除のイベントはすべてのアプリにはブロードキャストされず、現在実行中のアプリにのみ送信されます。