2011年5月15日日曜日

Android USB Accessory

USB Accessory

USB accessory モードによって、Android-powered デバイス向けにデザインされた USB host ハードウェアに接続することができます。accessory には Android Accessory Development Kit ドキュメントで説明されている Android accessory プロトコルが付いている必要があります。これにより、USB host として振舞うことができない Android-powered デバイスが USB ハードウェアとやりとりすることができます。Android-powered デバイスが USB accessory モードの場合、接続された Android USB accessory はホストして振る舞い、USB バスに電力を供給し、接続されているデバイスを列挙します。Android 3.1 (API level 12) は USB accessory mode をサポートします。より広い範囲のデバイスでサポートできるように、この機能は Android 2.3.4 (API level 10) にもバックポートされています。

---

Choosing the Right USB Accessory APIs


USB accessory APIs は Android 3.1 プラットフォームで紹介されましたが、Google APIs add-on library を使うことで Android 2.3.4 でも使うことができます。これらの API は外部ライブラリを使うようにバックポートされているため、USB accessory mode をサポートするためにインポートできるパッケージが2つ存在します。サポートしたい Android-powered デバイスに応じて、次のどちらかを使ってください。

  • com.android.future.usb: Android 2.3.4 で USB accessory mode をサポートするためのパッケージで、バックポートされた USB accessory API を含む Google APIs add-on library は、このネームスペース内に入っています。
    add-on library を使って書かれたアプリケーションをサポートするために、Android 3.1 でもこのネームスペース内のクラスのインポートと呼び出しはサポートされています。
    この add-on library は android.hardware.usb accessory API 周りの薄いラッパーで、USB host mode はサポートしていません。
    USB accessory mode をサポートするデバイスをもっとも広い範囲でサポートしたい場合、add-on library を使って、このパッケージをインポートしてください。重要な注意事項として、すべての Android 2.3.4 デバイスが USB accessory 機能をサポートするよう要求されているわけではない、ということがあります。個々のデバイスメーカーがこの機能をサポートするかどうか決定します。これがマニフェストファイルで宣言しなければならない理由です。

  • android.hardware.usb: このネームスペースには、Android 3.1 で USB accessory mode をサポートするためのクラスが含まれています。このパッケージはフレームワーク API の一部として含まれているため、Android 3.1 では USB accessory mode をサポートするのに add-on library を使う必要はありません。
    USB accessory mode をサポートするためのハードウェアを持った Android 3.1 以上のデバイスのみ対象とする場合(この条件はマニフェストファイルで宣言できます)、このパッケージを使ってください。



Installing the Google APIs add-on library

add-on をインストールするには、SDK Manager で Google APIs Android API 10 パッケージをインストールします。add-on library のインストールのより詳しい情報は Installing the Google APIs Add-on を参照してください。

---

API Overview


add-on library はフレームワーク API のラッパーなので、USB accessory 機能をサポートするクラスは似たものになります。そのため、add-on library を使っている場合でも android.hardware.usb のリファレンスドキュメントを使うことができます。

注意: しかし、add-on library とフレームワーク API の間には、気をつけておくべき使い方の違いがあります。

次の表は USB accessory API をサポートするクラスを表しています。

ClassDescription
UsbManager接続された USB accessory の列挙と通信を許可する
UsbAccessoryUSB accessory を表し(represents)、自身の識別情報にアクセスするメソッドを含む



Usage differences between the add-on library and platform APIs

Google APIs add-on library とプラットフォーム API の間の使い方の違いは2つあります。

add-on library を使っている場合、UsbManager オブジェクトは次の方法で取得します。

UsbManager manager = UsbManager.getInstance(this);


add-on library を使わない場合、次の方法で UsbManager オブジェクトを取得します。

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);



intent filter を使って接続された accessory をフィルターする場合、UsbAccessory オブジェクトはインテント内に含まれた状態でアプリケーションに渡されます。add-on library を使う場合、次の方法で UsbAccessory オブジェクトを取得します。

UsbAccessory accessory = UsbManager.getAccessory(intent);


add-on library を使わない場合、次の方法で UsbAccessory オブジェクトを取得します。

UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);


---

Android Manifest requirements


次のリストは、USB accessory API 動かす前に、アプリケーションのマニフェストファイルに追加する必要がある項目です。これらのアイテムの宣言例は manifest and resource file examples にあります。

  • すべての Android-powered デバイスが USB accessory APIs をサポートする義務(guaranteed) があるわけではないので、アプリケーションが android.hardware.usb.accessory 機能を使うことを宣言した <uses-feature> エレメントを含むようにする

  • add-on library を使う場合は、com.android.feature.usb.accessory を指定した <uses-library> エレメントを追加する

  • アプリケーションの最小 SDK を、add-on library を使う場合は API Level 10 に、android.hardware.usb パッケージを使う場合は API Level 12 に設定する

  • USB accessory が取り付けられたときのアプリケーションに通知したい場合は、android.hardware.usb.action.USB_ACCESSORY_ATTACHED インテント用の <intent-filter><meta-data> エレメントのペアをメインの Activity に指定する
    <meta-data> エレメントでは、検出した accessory の識別情報を宣言した外部の XML リソースを示す

    XML リソースファイルでは、フィルターしたい accessory 用の <usb-accessory> エレメント を宣言する
    <ues-accessory> は次の属性を持つことができる

     ・ manufacturer
     ・ model
     ・ version

    リソースファイルは /res/xml ディレクトリに保存する
    リソースファイルの名前 (.xml 拡張子を除いた部分) は <meta-data> エレメントで指定したものと同じでなければならない
    XML リソースファイルの形式も次の example にある



Manifest and resource file examples

次の例はサンプルマニフェストとそれに対応するリソースファイルです。

<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 に保存されているべきであり、model, manifacturer, version に応じてフィルターされるすべての accessory を指定します。accessory はこれらの属性を Android-powered デバイスに送ります。

<?xml version="1.0" encoding="utf-8"?>

<resources>
<usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/>
</resources>


---

Working with accessories


ユーザーが USB accessory を Android-powered デバイスに接続したとき、Android のシステムは接続された accessory にあなたのアプリケーションが反応したいかどうか決めることができます。その場合、望むのであれば accessory との通信をセットアップすることができます。このようにするには、アプリケーションは

  • 1. accessory が取り付けられたイベントをフィルターする intent filter を使うか、接続された accessory を列挙して適切なものを選択するかして 接続された accessory を見つける

  • 2. accessory と通信するためのパーミッションをユーザーに尋ねる(まだパーミッションを得られていない場合)

  • 3. 適切なインタフェースエンドポイントでデータを読み書きすることで accessory と通信する



Discovering an accessory

アプリケーションは、ユーザーが accessory を接続したときに知らせてくれる intent filter を使うか、もしくはすでに接続されている accessory を列挙するか、のいずれかの方法をつかって accessory を見つけることができます。アプリケーションが意図している accessory を自動的に検出したい場合、intent filter を使うことはとても便利です。intent をフィルターせず、すべての接続されている accessory のリストを取得したい場合、接続されている accessory の列挙は便利です。


Using an intent filter

アプリケーションで特定の USB accessory を見つけるために、android.hardware.usb.action.USB_ACCESSORY_ATTACHED intent に対してフィルターする intent filter を指定することができます。この intent filter と一緒に USB accessory のプロパティ(manufacturer, model, version)を宣言したリソースファイルを指定する必要があります。

intent filter の定義は、例えば次のようになります。

<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>


対応するリソースファイルは、例えば次のようになります。

<?xml version="1.0" encoding="utf-8"?>

<resources>
<usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" />
</resources>


activity 内では、取り付けられた accessory を表す UsbAccessory を次のようにして intent から取得することができます。

add-on library を使う場合

UsbAccessory accessory = UsbManager.getAccessory(intent);


プラットフォーム API を使う場合

UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);



Enumerating accessories

アプリケーションが起動している間、自身の識別情報をもった accessory を列挙することができます。

getAccessoryList() メソッド使って、接続されている全ての USB accsessory の配列を取得するには次のようにします。

add-on library を使う場合

UsbManager manager = UsbManager.getInstance(this);
UsbAccessory[] accessoryList = manager.getAcccessoryList();


プラットフォーム API を使う場合

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbAccessory[] accessoryList = manager.getAcccessoryList();


注意: 現在は1回につき1個の接続された accessory のみサポートしていますが、API は将来的に複数の accessory をサポートするようにデザインされています。


Obtaining permission to communicate with an accessory

USB accessory と通信する前に、アプリケーションはユーザーから許可を得る必要があります。

注意: アプリケーションが intent filter を使って接続された accessory を見つける場合、アプリケーションがその intent を処理することをユーザーが許可すると、自動的にパーミッションを受信します。そうではない場合、accessory に接続する前にアプリケーション内で明示的にパーミッションをリクエストしなければなりません。

アプリケーションですでに接続されている accessory を enumarate して、いずれかの1つと通信したい場合など、いくつかの状況で明示的にパーミッションを聞く必要があります。accessory と通信を試みる前に、accessory にアクセスするパーミッションをチェックしなければなりません。そうしないと、ユーザーが accessory へのアクセスを拒否した場合にランタイムエラーを受信するでしょう。

明示的にパーミッションを得るために、まず broadcast receiver を作成します。この receiver では requestPermission() を呼んだときに broadcast を取得する intent を listen するようにします。requestPermission() を呼ぶとユーザーに accessory に接続するかどうかのパーミッションを聞くダイアログが表示されます。
例えば、次のようにして broadcast receiver を作成します。

private static final String ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
private final BroadcastReceiver mUsbReceiver = 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);
}
}
}
}
};


broadcast receiver を登録するには、これを Activity の onCreate() メソッド内に入れます:

UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
private static final String ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
...
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);


accessory への接続パーミッションを尋ねるダイアログを表示するには、requestPermission() メソッドを呼びます:

UsbAccessory accessory;
...
mUsbManager.requestPermission(accessory, mPermissionIntent);


ユーザーがダイアログに返答すると、broadcast receiver は答えを表す boolean の EXTRA_PERMISSION_GRANTED extra を含む intent を受信します。accessory に接続するまえに、この extra が true になっているかチェックします。


Communicating with an accessory

UsbManager を使って file descripter を取得することで accessory と通信することができます。この file descripter によって、データを読み書きするための input / ouput stream をセットアップすることができます。stream は accessory の input / output bulk endpoint を表します。メイン UI スレッドをロックしないために、デバイスと accessory 間の通信のセットアップは別のスレッドで行うべきです。
例えば、次のようにしてセットアップを行います。

UsbAccessory mAccessory;
ParcelFileDescriptor mFileDescriptor;
FileInputStream mInputStream;
FileOutputStream mOutputStream;

...

private void openAccessory() {
Log.d(TAG, "openAccessory: " + accessory);
mFileDescriptor = mUsbManager.openAccessory(mAccessory);
if (mFileDescriptor != null) {
FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
Thread thread = new Thread(null, this, "AccessoryThread");
thread.start();
}
}


スレッドの run() メソッドで、FileInputStreamFileOutputStream オブジェクトを使って accessory へ読み書きすることができます。FileInputStream オブジェクトで accessory からデータを読み出すとき、USB パケットデータを保持するのに十分な大きさのバッファーを確保するようにしてください。Android accessory プロトコルは、最大で 16385 bytes までのパケットバッファーをサポートします。そのため、簡単化のための選択肢として、常にこのサイズのバッファーを宣言することもできます。

注意: lower level では、パケットは USB full-speed accessory に対して 64 bytes, USB high-speed accessory に対して 512 bytes です。Android accessory プロトコルは、簡単化のためにこれら両方の速度のパケットを合わせて1つのロジカルなパケットに bundle します。

Android でスレッドを使うためのより詳しい情報は Processes and Threads にあります。


Terminating communication with an accessory

accessory との通信が完了した、もしくは accessory が取り外されたときには、close() を呼んで開いた file descriptor を閉じます。取り外されたイベントを察知するには、次のような broadcast receiver を作成します:

BroadcastReceiver mUsbReceiver = 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
}
}
}
};


マニフェストではなく、アプリケーション内で broadcast receiver を作成することで、アプリケーションが起動している間だけ取り外しイベントを処理できるようになります。これにより、取り外しイベントが全てのアプリケーションに broadcast されることなく、現在起動しているアプリケーションにのみ送られます。


 

0 件のコメント:

コメントを投稿