Register now for Android Dev Summit 2019!

LoWPAN

The Android Things LoWPAN APIs enable apps to interact with local devices connected over a wireless personal area network. Using LoWPAN user drivers, your apps can extend this framework and add new LoWPAN interfaces connected over Peripheral I/O.

Interface hardware connected through a LoWPAN user driver must implement a Network Control Processor (NCP) that supports the Spinel protocol. This is the standard communications protocol used by OpenThread devices.

Adding the required permission

Add the required permission for the user driver to your app's manifest file:

<uses-permission android:name="com.google.android.things.permission.MANAGE_LOWPAN_INTERFACES" />

Implementing the driver

To define a new LoWPAN interface, extend the LowpanDriver class and override the methods to manage the hardware connection:

Kotlin

import com.google.android.things.userdriver.lowpan.LowpanDriver
import com.google.android.things.userdriver.lowpan.LowpanDriverCallback

class NcpLowpanDriver : LowpanDriver() {

    ...

    override fun start(lowpanDriverCallback: LowpanDriverCallback?) {
        /* Open connection to interface hardware */
    }

    override fun stop() {
        /* Close connection to interface hardware */
    }

    override fun reset() {
        /* Send a reset command or power cycle the radio interface */
    }

    override fun sendFrame(bytes: ByteArray?) {
        /* Transmit a data frame to the radio interface */
    }
}

Java

import com.google.android.things.userdriver.lowpan.LowpanDriver;
import com.google.android.things.userdriver.lowpan.LowpanDriverCallback;
...

public class NcpLowpanDriver extends LowpanDriver {

    ...

    @Override
    public void start(LowpanDriverCallback lowpanDriverCallback) {
        /* Open connection to interface hardware */
    }

    @Override
    public void stop() {
        /* Close connection to interface hardware */
    }

    @Override
    public void reset() {
        /* Send a reset command or power cycle the radio interface */
    }

    @Override
    public void sendFrame(byte[] bytes) {
        /* Transmit a data frame to the radio interface */
    }
}

Managing the device connection

When the framework registers your new driver, it passes a LowpanDriverCallback instance in the start() method. Use this callback to report asynchronous events from the hardware back to the framework service.

Once your driver has opened a connection to your interface hardware, report to the framework that initialization is complete by calling onStarted() callback method. Report any errors that occur communicating with the interface hardware via the onError() callback method.

Kotlin

import com.google.android.things.userdriver.lowpan.LowpanDriver
import com.google.android.things.userdriver.lowpan.LowpanDriverCallback
...

class NcpLowpanDriver : LowpanDriver() {

    private var callback: LowpanDriverCallback? = null

    ...

    override fun start(lowpanDriverCallback: LowpanDriverCallback?) {
        callback = lowpanDriverCallback

        try {
            /* Open connection to interface hardware */

            // Report completion to the framework
            callback?.onStarted()
        } catch (e: IOException) {
            // Report error to the framework
            callback?.onError(HAL_ERROR_IOFAIL)
        }
    }
}

Java

import com.google.android.things.userdriver.lowpan.LowpanDriver;
import com.google.android.things.userdriver.lowpan.LowpanDriverCallback;
...

public class NcpLowpanDriver extends LowpanDriver {

    private LowpanDriverCallback callback;

    ...

    @Override
    public void start(LowpanDriverCallback lowpanDriverCallback) {
        callback = lowpanDriverCallback;

        try {
            /* Open connection to interface hardware */

            // Report completion to the framework
            callback.onStarted();
        } catch (IOException e) {
            // Report error to the framework
            callback.onError(HAL_ERROR_IOFAIL);
        }
    }
}

Your driver should also monitor the interface hardware for reset events. If the hardware is reset for any reason, report this to the framework via LowpanDriverCallback.onReset().

Transferring data

The framework delivers data frames to the driver using the sendFrame() method. Pass each frame onto the interface hardware, and report any incoming frames from the hardware via onReceiveFrame().

Kotlin

import com.google.android.things.userdriver.lowpan.LowpanDriver
import com.google.android.things.userdriver.lowpan.LowpanDriverCallback
...

class NcpLowpanDriver : LowpanDriver() {

    private var callback: LowpanDriverCallback? = null

    ...

    override fun sendFrame(bytes: ByteArray?) {
        try {
            /* Transmit a data frame to the radio interface */
        } catch (e: IOException) {
            // Report error to the framework
            callback?.onError(HAL_ERROR_IOFAIL)
        }
    }

    /* Interface hardware generated a new data frame */
    private fun onDataReceived(data: ByteArray) {
        // Report data to the framework
        callback?.onReceiveFrame(data)
    }
}

Java

import com.google.android.things.userdriver.lowpan.LowpanDriver;
import com.google.android.things.userdriver.lowpan.LowpanDriverCallback;
...

public class NcpLowpanDriver extends LowpanDriver {

    private LowpanDriverCallback callback;

    ...

    @Override
    public void sendFrame(byte[] bytes) {
        try {
            /* Transmit a data frame to the radio interface */
        } catch (IOException e) {
            // Report error to the framework
            callback.onError(HAL_ERROR_IOFAIL);
        }
    }

    /* Interface hardware generated a new data frame */
    private void onDataReceived(byte[] data) {
        // Report data to the framework
        callback.onReceiveFrame(data);
    }
}

Registering the interface

Connect your new radio interface to the framework by registering it with the UserDriverManager:

Kotlin

import com.google.android.things.userdriver.UserDriverManager

class LowpanDriverService : Service() {

    lateinit var driver: NcpLowpanDriver

    override fun onCreate() {
        super.onCreate()
        ...
        val manager = UserDriverManager.getInstance()

        driver = NcpLowpanDriver()
        // Register the new driver with the framework
        manager.registerLowpanDriver(driver)
    }

    override fun onDestroy() {
        super.onDestroy()
        ...
        val manager = UserDriverManager.getInstance()
        // Unregister the driver when finished
        manager.unregisterLowpanDriver(driver)
    }
}

Java

import com.google.android.things.userdriver.UserDriverManager;
...

public class LowpanDriverService extends Service {

    NcpLowpanDriver driver;

    @Override
    public void onCreate() {
        super.onCreate();
        ...
        UserDriverManager manager = UserDriverManager.getInstance();

        driver = new NcpLowpanDriver();
        // Register the new driver with the framework
        manager.registerLowpanDriver(driver);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        ...
        UserDriverManager manager = UserDriverManager.getInstance();
        // Unregister the driver when finished
        manager.unregisterLowpanDriver(driver);
    }
}

With the driver properly registered, apps can form or join local networks and communicate with connected devices using the Android LoWPAN API.