User Driverを使って、電子部品とAndroid Frameworkをつなぐ


AndroidThingsでは、デバイスごとにつけているセンサーやGPS、周辺機器が異なるため、一部のAndroid Frameworkで利用できる機能がデフォルトでは使えなくなっています。

例えばGPSはRaspberry Piには乗っていないため、GPSに関連する機能はデフォルトでは使えません。
ただし、電子部品として追加する分にはGPS等をつけることは可能です。

この時、追加したGPSや温度センサー等をAndroid Frameworkで利用できるようにするのがUser Driverです。
※ドキュメントだと、User-Space Drivers、User Driversと若干表記ゆれがある気がします.

 https://developer.android.com/things/sdk/drivers/index.html
 
## User Driverのメリット
 
 主にUser Driverのメリットには以下があります。
 
 * 移植性
 * 再利用性
 * 統合

色々メリットはありますが、単純に「様々な周辺機器ラップしてAndroid Framework上から触れたほうが、既存のコードやライブラリも使えるし、移植性も高いでしょ」という感じでイメージすればいいと思います。

## User Driverの種類

主に3種類あります。

* GPS
	* GPSです。AndroidのLocation Serviceと統合できます。
* HID - Human Interface Devices
	* ユーザの入力を扱うDriverです。AndroidのKey EventsやMotion Events(Touchとか)を統合することが出来ます。
	* 例えば物理ボタンとKey Eventsを紐付けたり出来ます。
* Sensor
	* センサーです。 AndroidのSensorManagerと統合できます。
	* PIOで追加した様々なセンサーをAndroid Framework上(SensorManager)経由で扱う事ができるようになります。


## User Driverを試してみる

とりあえず一番簡単なボタンのUser Driverを試してみます。めんどくさいので公式の[sample-button](https://github.com/androidthings/sample-button)をそのまま使います。


### ハードウェア構成

以下です。

![userdriver.png](https://qiita-image-store.s3.amazonaws.com/0/2965/035ccf5e-02b7-4888-ffb5-536701a87da9.png "userdriver.png")



### コード
一応kotlinにして、いらないところやコメントを追加してあります。

```kotlin:MainActivity.kt
package com.github.soundtricker.androidthings.samleuserdriver

import android.app.Activity
import android.os.Bundle
import java.io.IOException
import android.util.Log
import android.view.KeyEvent
import com.google.android.things.contrib.driver.button.Button
import com.google.android.things.contrib.driver.button.ButtonInputDriver
import com.google.android.things.pio.Gpio
import com.google.android.things.pio.PeripheralManagerService


class MainActivity : Activity() {

    companion object {
        private val TAG = MainActivity::class.java.simpleName
        private val GPIO_BUTTON_PIN_NAME= "BCM21"
        private val GPIO_LED_PIN_NAME = "BCM6"
    }

    private var mButtonInputDriver: ButtonInputDriver? = null
    private var mLedGpio: Gpio? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setupButton()
        setupLed()
    }

    override fun onDestroy() {
        super.onDestroy()
        destroyButton()
        destroyLed()
    }

    private fun setupButton() {
        try {
            mButtonInputDriver = ButtonInputDriver(GPIO_BUTTON_PIN_NAME,
                    Button.LogicState.PRESSED_WHEN_LOW,
                    KeyEvent.KEYCODE_SPACE
            )
            mButtonInputDriver!!.register()
        } catch (e: IOException) {
            Log.e(TAG, "failed initialize button driver", e)
            return
        }
    }
    private fun setupLed() {
        try {
            val pioSerivce = PeripheralManagerService()
            mLedGpio = pioSerivce.openGpio(GPIO_LED_PIN_NAME)
        } catch (e: IOException) {
            Log.e(TAG, "failed initialize led", e)
            return
        }
    }


    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        if (keyCode == KeyEvent.KEYCODE_SPACE) {
            // Turn on the LED
            setLedValue(true)
            return true
        }

        return super.onKeyDown(keyCode, event)
    }

    override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
        if (keyCode == KeyEvent.KEYCODE_SPACE) {
            // Turn off the LED
            setLedValue(false)
            return true
        }

        return super.onKeyUp(keyCode, event)
    }

    private fun setLedValue(value: Boolean) {
        try {
            mLedGpio!!.value = value
        } catch (e: IOException) {
            Log.e(TAG, "Error updating GPIO value", e)
        }

    }

    private fun destroyButton() {
        Log.i(TAG, "Closing button")
        mButtonInputDriver?.unregister()
        try {
            mButtonInputDriver?.close()
        } catch (e: IOException) {
            Log.e(TAG, "Error closing Button driver", e)
        } finally {
            mButtonInputDriver = null
        }

    }
    private fun destroyLed() {
        try {
            mLedGpio!!.close()
        } catch (e: IOException) {
            Log.e(TAG, "Error closing LED", e)
        } finally {
            mLedGpio = null
        }
    }}


```

上の

```kotlin
mButtonInputDriver = ButtonInputDriver(GPIO_BUTTON_PIN_NAME,
                    Button.LogicState.PRESSED_WHEN_LOW,
                    KeyEvent.KEYCODE_SPACE
            )
            mButtonInputDriver!!.register()
```

部分がAndroid FrameworkにUser Driverを登録している部分です。
Android FrameworkとしてUser Driverを登録することで以下のように物理Buttonの押し離しをKey Eventとして、扱う事ができるようになります。

```kotlin
    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        if (keyCode == KeyEvent.KEYCODE_SPACE) {
            // Turn on the LED
            setLedValue(true)
            return true
        }

        return super.onKeyDown(keyCode, event)
    }

    override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
        if (keyCode == KeyEvent.KEYCODE_SPACE) {
            // Turn off the LED
            setLedValue(false)
            return true
        }

        return super.onKeyUp(keyCode, event)
    }
```

なお、上記コードを動かすためには、AndroidManifest.xmlに以下を追加する必要があります。

```xml:AndroidManifest.xml
<uses-permission android:name="com.google.android.things.permission.MANAGE_INPUT_DRIVERS" />
```


## まとめ

User Driverによって、Androidらくしセンサーなどを扱えるのはAndroid Thingsのメリットだと思います。
多分