AndroidでUSBデバイスを操作する

コメントのGeektimesに関する最近の記事で、USBバスに接続されたAndroid OSの周辺機器のサポートについて質問がありました。 実際、たとえばプリンターやMFPを操作するためのほとんどのベンダーソフトウェアは、ネットワーク接続のみをサポートしています。 ただし、これはAndroid OS自体にそのような可能性がないことを意味するものではありません-それは、ほとんどのデバイスが完全なUSBホストを持たず、すべてがOTGをサポートしていないことを意味します。 ネットワーク上では、例外なくすべてのユーザーが作業できます。

OTGポートを備えたほとんどのAndroidデバイスは、システムレベルで次のデバイスクラスをサポートします(Linuxカーネルまたは標準のAndroidコンポーネント)。


やや少ない頻度:


ハブは完全なホストポートでサポートされていますが、OTGポートではサポートされていません。

Linuxカーネルレベルでサポートされるデバイスの詳細なリストについては、sysfsを参照してください。

$ ls /sys/bus/usb/drivers

モジュールが原則としてLinuxカーネルのソースコードで利用可能であるがAndroidに含まれていない場合、すべてのターゲットシステムにアセンブルおよび配置できるという事実に依存するべきではありません。

ただし、Android 3.1(API 12)以降、システムにはアプリケーションレベルでUSB周辺機器をサポートするのに十分なツールが含まれています。 これらのツールについては、Android APIガイドのUSBホストのセクションで説明しています 。 ここでは、いくつかのタイプのデバイスを使用した実際の作業の例を示します。

アクセス権


他のアクションに関しては、AndroidはアプリケーションがUSB周辺機器にアクセスする許可を取得することを要求します。 この許可を取得するには2つの方法があります。


ユーザーへの不要な質問は私のタスクにとって望ましくないため、最初の方法を使用しました。

そのため、マニフェストに次を追加する必要があります。

 <activity ...> ... <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" /> </activity> <uses-feature android:name="android.hardware.usb.host" /> <uses-sdk android:minSdkVersion="12" /> 

そして、res / xml / device_filter.xmlに以下を書きます:

 <?xml version="1.0" encoding="utf-8"?> <resources> <!-- Serial converters --> <!-- 0x0403 / 0x6001: FTDI FT232R UART --> <usb-device vendor-id="1027" product-id="24577" /> <!-- … more devices … --> </resources> 

16進数システムではVID:PIDを示すことは一般に受け入れられていますが、ここでは10進数で示す必要があります。 ドキュメントには、VIDとPIDなしでクラスのみを指定することが可能であると記載されていますが、これは私にとってはうまくいきませんでした。

プリンター


例としてプリンターを使用して、android.hardware.usb APIを直接使用する方法を示します。 データ転送レベルでは、すべてのプリンターが標準クラスのUSBデバイスをサポートしています。

 int UsbConstants.USB_CLASS_PRINTER = 7; 

クラスは非常に単純です。 このクラス内で、デバイスは以下をサポートする必要があります。


 int GET_DEVICE_ID = 0; int GET_PORT_STATUS = 1; int SOFT_RESET = 2; 

以下のコードは、Linuxの/ dev / usb / lpデバイスに類似した機能を提供します。 次に、ソースドキュメントを特定のプリンターモデルにとって明確なデータパケットに変換するフィルターが必要です。 しかし、これは別の記事のトピックです。 1つのオプションとして、NDKを使用してghostscriptを構築できます。

デバイスを使用するには、最初に次のものが必要です。

1.デバイスを見つけます。 例では、簡単にするために、最初のものを探しています:

 UsbDevice findDevice() { for (UsbDevice usbDevice: mUsbManager.getDeviceList().values()) { if (usbDevice.getDeviceClass() == UsbConstants.USB_CLASS_PRINTER) { return usbDevice; } else { UsbInterface usbInterface = findInterface(usbDevice); if (usbInterface != null) return usbDevice; } } return null; } UsbInterface findInterface(UsbDevice usbDevice) { for (int nIf = 0; nIf < usbDevice.getInterfaceCount(); nIf++) { UsbInterface usbInterface = usbDevice.getInterface(nIf); if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_PRINTER) { return usbInterface; } } return null; } UsbDevice mUsbDevice = findDevice(); UsbInterface mUsbInterface = findInterface(mUsbDevice); 

2.エンドポイントを取得します。

 for (int nEp = 0; nEp < mUsbInterface.getEndpointCount(); nEp++) { UsbEndpoint tmpEndpoint = mUsbInterface.getEndpoint(nEp); if (tmpEndpoint.getType() != UsbConstants.USB_ENDPOINT_XFER_BULK) continue; if ((mOutEndpoint == null) && (tmpEndpoint.getDirection() == UsbConstants.USB_DIR_OUT)) { mOutEndpoint = tmpEndpoint; } else if ((mInEndpoint == null) && (tmpEndpoint.getDirection() == UsbConstants.USB_DIR_IN)) { mInEndpoint = tmpEndpoint; } } if (mOutEndpoint == null) throw new IOException("No write endpoint: " + deviceName); 

3.デバイスを直接開きます。

 mConnection = mUsbManager.openDevice(mUsbDevice); if (mConnection == null) throw new IOException("Can't open USB connection:" + deviceName); mConnection.claimInterface (mUsbInterface, true); 

4.その後、デバイスの読み取りと書き込みができます。

 public int read(final byte[] data) throws IOException { int size = Math.min(data.length, mInEndpoint.getMaxPacketSize()); return mConnection.bulkTransfer(mInEndpoint, data, size, getReadTimeout()); } public int write(final byte[] data, final int length) throws IOException { int offset = 0; while (offset < length) { int size = Math.min(length - offset, mInEndpoint.getMaxPacketSize()); int bytesWritten = mConnection.bulkTransfer(mOutEndpoint, Arrays.copyOfRange(data, offset, offset + size), size, getWriteTimeout()); if (bytesWritten <= 0) throw new IOException("None written"); offset += bytesWritten; } return offset; } 

5.完了したら、デバイスを閉じます。

 mConnection.close(); 

USBシリアルコンバーター


プリンターとは異なり、USBシリアルコンバーターははるかに標準化されていません。 シリアルポートのパラメータの設定が大幅に異なるいくつかの一般的なチップがあります-ビットレート、パリティなど。 幸いなことに、既存のほぼすべてのチップをサポートするライブラリgithub.com/mik3y/usb-serial-for-androidがあります。 ライブラリはUSB APIを完全に隠し、必要なすべてのアクションを最小限のパラメーターで最小限の呼び出しに最小化します。

1.デバイスを見つけて開きます。

 UsbSerialPort mUsbSerialPort; UsbManager mUsbManager = (UsbManager) DEVICE.getSystemService(Context.USB_SERVICE); String type = “FTDI”; for (UsbDevice usbDevice: mUsbManager.getDeviceList().values()) { UsbSerialDriver usbSerialDriver = UsbSerialProber.probeSingleDevice(usbDevice); if (usbSerialDriver == null) continue; if (!type.equals(usbSerialDriver.getShortDeviceName())) continue; mUsbSerialPort = usbSerialDriver.getPort(0); mUsbSerialPort.open(mUsbManager); break; } 

2.シリアルポートのパラメーターを設定します。

 mUsbSerialPort.setParameters(baudRate, dataBits, stopBits, parity); 

3.ポートの読み取りと書き込み:

 public int read(final byte[] data) throws IOException { return mUsbSerialPort.read(data, getReadTimeout()); } public int write(final byte[] data, final int length) throws IOException { return mUsbSerialPort.write(data, length, getWriteTimeout()); } 

4.完了したら、ポートを閉じます。

 mUsbSerialPort.close(); 

まとめ


USB周辺機器での作業が非常にシンプルで論理的であることを示すことができたことを願っています。 もちろん、特定のデバイスのプロトコルの実装は単純ではありませんが、どのシステムでも同じ程度に現れます。

上記のすべての例を実際のプロジェクトから取得し、明らかなチェックのみを除外し、重要な行のみを残しました。

Source: https://habr.com/ru/post/J277093/


All Articles