libandroid_net_lowpan: Reintroduce new unit tests for data classes

This change introduces new unit tests which confirm that various data
classes are being property marshalled between their Java and C++
implementations.

This was originally added to platform/frameworks/base via change id
I41d590b1e77dc41873c4b9e9bf1b7f1bf859f74e, but that change was
reverted (via ag/2553741) due the failure of build 4190632 on the
branch `git_wear-master`. Moving these specific tests into this
repository avoids this failure case.

This change is dependent on the following change id:

    I70fe6be8d71424e11a537daaa69c3f6cfae8e49d

Bug: b/63707448 b/63708348
Change-Id: I41d590b1e77dc41873c4b9e9bf1b7f1bf859f74e
Test: These tests both compile and pass
diff --git a/libandroid_net_lowpan/Android.mk b/libandroid_net_lowpan/Android.mk
index 730d97d..a7bef1f 100644
--- a/libandroid_net_lowpan/Android.mk
+++ b/libandroid_net_lowpan/Android.mk
@@ -31,3 +31,5 @@
 LOCAL_SRC_FILES += $(call all-Iaidl-files-under, ../../../../base/lowpan/java/android/net/lowpan)
 LOCAL_SRC_FILES += $(call all-cpp-files-under)
 include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libandroid_net_lowpan/tests/Android.mk b/libandroid_net_lowpan/tests/Android.mk
new file mode 100644
index 0000000..ee44bc5
--- /dev/null
+++ b/libandroid_net_lowpan/tests/Android.mk
@@ -0,0 +1,114 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+# Make test APK
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+# This list is generated from the java source files in this module
+# The list is a comma separated list of class names with * matching zero or more characters.
+# Example:
+#   Input files: src/com/android/server/lowpan/Test.java src/com/android/server/lowpan/AnotherTest.java
+#   Generated exclude list: com.android.server.lowpan.Test*,com.android.server.lowpan.AnotherTest*
+
+# Filter all src files to just java files
+local_java_files := $(filter %.java,$(LOCAL_SRC_FILES))
+# Transform java file names into full class names.
+# This only works if the class name matches the file name and the directory structure
+# matches the package.
+local_classes := $(subst /,.,$(patsubst src/%.java,%,$(local_java_files)))
+# Utility variables to allow replacing a space with a comma
+comma:= ,
+empty:=
+space:= $(empty) $(empty)
+# Convert class name list to jacoco exclude list
+# This appends a * to all classes and replace the space separators with commas.
+# These patterns will match all classes in this module and their inner classes.
+jacoco_exclude := $(subst $(space),$(comma),$(patsubst %,%*,$(local_classes)))
+
+jacoco_include := android.net.lowpan.*
+
+LOCAL_JACK_COVERAGE_INCLUDE_FILTER := $(jacoco_include)
+LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+	android-support-test \
+	guava \
+	mockito-target-minus-junit4 \
+	frameworks-base-testutils \
+
+LOCAL_JNI_SHARED_LIBRARIES += libframeworkslowpantestsjni
+LOCAL_JNI_SHARED_LIBRARIES += libandroid_net_lowpan
+LOCAL_JNI_SHARED_LIBRARIES += \
+    libbacktrace \
+    libbase \
+    libbinder \
+    libc++ \
+    libcutils \
+    liblog \
+    liblzma \
+    libnativehelper \
+    libnetdaidl \
+    libui \
+    libunwind \
+    libutils \
+    libvndksupport \
+    libcrypto \
+    libhidl-gen-utils \
+    libhidlbase \
+    libhidltransport \
+    libpackagelistparser \
+    libpcre2 \
+    libselinux \
+    libtinyxml2 \
+    libvintf \
+    libhwbinder \
+    android.hidl.token@1.0
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_PACKAGE_NAME := FrameworksLowpanApiNativeTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_CERTIFICATE := platform
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_PACKAGE)
+
+#########################################################################
+# Build JNI Shared Library
+#########################################################################
+
+LOCAL_PATH:= $(LOCAL_PATH)/jni
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_CFLAGS := -Wall -Wextra -Werror
+
+LOCAL_SRC_FILES := $(call all-cpp-files-under)
+
+LOCAL_SHARED_LIBRARIES += libandroid_net_lowpan
+LOCAL_SHARED_LIBRARIES += libbinder
+LOCAL_SHARED_LIBRARIES += liblog
+
+LOCAL_MODULE := libframeworkslowpantestsjni
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libandroid_net_lowpan/tests/AndroidManifest.xml b/libandroid_net_lowpan/tests/AndroidManifest.xml
new file mode 100644
index 0000000..e9d71ef
--- /dev/null
+++ b/libandroid_net_lowpan/tests/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.net.lowpan.testnative">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:label="LowpanTestDummyLabel"
+                  android:name="LowpanTestDummyName">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.net.lowpan.testnative"
+        android:label="Frameworks LoWPAN API Native Tests">
+    </instrumentation>
+
+</manifest>
diff --git a/libandroid_net_lowpan/tests/AndroidTest.xml b/libandroid_net_lowpan/tests/AndroidTest.xml
new file mode 100644
index 0000000..55e5e7f
--- /dev/null
+++ b/libandroid_net_lowpan/tests/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Frameworks LoWPAN API Native Tests.">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="FrameworksLowpanApiTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-tag" value="FrameworksLowpanApiNativeTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.net.lowpan.testnative" />
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+    </test>
+</configuration>
diff --git a/libandroid_net_lowpan/tests/README.md b/libandroid_net_lowpan/tests/README.md
new file mode 100644
index 0000000..0711cc1
--- /dev/null
+++ b/libandroid_net_lowpan/tests/README.md
@@ -0,0 +1,50 @@
+# libandroid_net_lowpan Unit Tests
+This package contains unit tests for libandroid_net_lowpan based on the
+[Android Testing Support Library](http://developer.android.com/tools/testing-support-library/index.html).
+The test cases are built using the [JUnit](http://junit.org/) and [Mockito](http://mockito.org/)
+libraries.
+
+## Running Tests
+The easiest way to run tests is simply run
+
+```
+frameworks/opt/net/lowpan/libandroid_net_lowpan/tests/runtests.sh
+```
+
+`runtests.sh` will build the test project and all of its dependencies and push the APK to the
+connected device. It will then run the tests on the device.
+
+To pick up changes in framework/base, you will need to:
+1. rebuild the framework library 'make -j32'
+2. sync over the updated library to the device 'adb sync'
+3. restart framework on the device 'adb shell stop' then 'adb shell start'
+
+To enable syncing data to the device for first time after clean reflash:
+1. adb disable-verity
+2. adb reboot
+3. adb remount
+
+See below for a few examples of options to limit which tests are run.
+See the
+[AndroidJUnitRunner Documentation](https://developer.android.com/reference/android/support/test/runner/AndroidJUnitRunner.html)
+for more details on the supported options.
+
+```
+runtests.sh -e package android.net.lowpan
+runtests.sh -e class android.net.lowpan.LowpanIdentityTest
+```
+
+If you manually build and push the test APK to the device you can run tests using
+
+```
+adb shell am instrument -w 'android.net.lowpan.testnative/android.support.test.runner.AndroidJUnitRunner'
+```
+
+## Adding Tests
+Tests can be added by adding classes to the src directory. JUnit4 style test cases can
+be written by simply annotating test methods with `org.junit.Test`.
+
+## Debugging Tests
+If you are trying to debug why tests are not doing what you expected, you can add android log
+statements and use logcat to view them. The beginning and end of every tests is automatically logged
+with the tag `TestRunner`.
diff --git a/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanBeaconInfoTest.java b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanBeaconInfoTest.java
new file mode 100644
index 0000000..6dbb3ed
--- /dev/null
+++ b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanBeaconInfoTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.lowpan;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertArrayEquals;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LowpanBeaconInfoTest {
+
+    static {
+        System.loadLibrary("frameworkslowpantestsjni");
+    }
+
+    private static native byte[] readAndWriteNative(byte[] inParcel);
+
+    public void testNativeParcelUnparcel(LowpanBeaconInfo original) {
+        byte[] inParcel = marshall(original);
+        byte[] outParcel = readAndWriteNative(inParcel);
+        LowpanBeaconInfo roundTrip = unmarshall(outParcel);
+
+        assertEquals(original, roundTrip);
+        assertArrayEquals(inParcel, outParcel);
+    }
+
+    @Test
+    public void testNativeParcelUnparcel() {
+        testNativeParcelUnparcel(
+                new LowpanBeaconInfo.Builder()
+                        .setName("TestNet1")
+                        .setPanid(0x1234)
+                        .setXpanid(
+                                new byte[] {
+                                    (byte) 0x00,
+                                    (byte) 0x11,
+                                    (byte) 0x22,
+                                    (byte) 0x33,
+                                    (byte) 0x44,
+                                    (byte) 0x55,
+                                    (byte) 0x66,
+                                    (byte) 0x77
+                                })
+                        .setType(LowpanInterface.NETWORK_TYPE_THREAD_V1)
+                        .setChannel(15)
+                        .setBeaconAddress(
+                                new byte[] {
+                                    (byte) 0x88,
+                                    (byte) 0x99,
+                                    (byte) 0xaa,
+                                    (byte) 0xbb,
+                                    (byte) 0xcc,
+                                    (byte) 0xdd,
+                                    (byte) 0xee,
+                                    (byte) 0xff
+                                })
+                        .build());
+
+        testNativeParcelUnparcel(
+                new LowpanBeaconInfo.Builder()
+                        .setName("TestNet2")
+                        .setPanid(0x5678)
+                        .setXpanid(
+                                new byte[] {
+                                    (byte) 0x88,
+                                    (byte) 0x99,
+                                    (byte) 0xaa,
+                                    (byte) 0xbb,
+                                    (byte) 0xcc,
+                                    (byte) 0xdd,
+                                    (byte) 0xee,
+                                    (byte) 0xff
+                                })
+                        .setType("bork-bork-bork")
+                        .setChannel(16)
+                        .setBeaconAddress(
+                                new byte[] {
+                                    (byte) 0x00,
+                                    (byte) 0x11,
+                                    (byte) 0x22,
+                                    (byte) 0x33,
+                                    (byte) 0x44,
+                                    (byte) 0x55,
+                                    (byte) 0x66,
+                                    (byte) 0x77
+                                })
+                        .setFlag(LowpanBeaconInfo.FLAG_CAN_ASSIST)
+                        .build());
+    }
+
+    /**
+     * Write a {@link LowpanBeaconInfo} into an empty parcel and return the underlying data.
+     *
+     * @see unmarshall(byte[])
+     */
+    private static byte[] marshall(LowpanBeaconInfo addr) {
+        Parcel p = Parcel.obtain();
+        addr.writeToParcel(p, /* flags */ 0);
+        p.setDataPosition(0);
+        return p.marshall();
+    }
+
+    /**
+     * Read raw bytes into a parcel, and read a {@link LowpanBeaconInfo} back out of them.
+     *
+     * @see marshall(LowpanBeaconInfo)
+     */
+    private static LowpanBeaconInfo unmarshall(byte[] data) {
+        Parcel p = Parcel.obtain();
+        p.unmarshall(data, 0, data.length);
+        p.setDataPosition(0);
+        return LowpanBeaconInfo.CREATOR.createFromParcel(p);
+    }
+}
diff --git a/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanChannelInfoTest.java b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanChannelInfoTest.java
new file mode 100644
index 0000000..eac4398
--- /dev/null
+++ b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanChannelInfoTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.lowpan;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertArrayEquals;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LowpanChannelInfoTest {
+
+    static {
+        System.loadLibrary("frameworkslowpantestsjni");
+    }
+
+    private static native byte[] readAndWriteNative(byte[] inParcel);
+
+    public void testNativeParcelUnparcel(LowpanChannelInfo original) {
+        byte[] inParcel = marshall(original);
+        byte[] outParcel = readAndWriteNative(inParcel);
+        LowpanChannelInfo roundTrip = unmarshall(outParcel);
+
+        assertEquals(original, roundTrip);
+        assertArrayEquals(inParcel, outParcel);
+    }
+
+    @Test
+    public void testNativeParcelUnparcel() {
+        int i;
+        for (i = 1; i < 26; i++) {
+            testNativeParcelUnparcel(LowpanChannelInfo.getChannelInfoForIeee802154Page0(i));
+        }
+    }
+
+    /**
+     * Write a {@link LowpanChannelInfo} into an empty parcel and return the underlying data.
+     *
+     * @see unmarshall(byte[])
+     */
+    private static byte[] marshall(LowpanChannelInfo addr) {
+        Parcel p = Parcel.obtain();
+        addr.writeToParcel(p, /* flags */ 0);
+        p.setDataPosition(0);
+        return p.marshall();
+    }
+
+    /**
+     * Read raw bytes into a parcel, and read a {@link LowpanChannelInfo} back out of them.
+     *
+     * @see marshall(LowpanChannelInfo)
+     */
+    private static LowpanChannelInfo unmarshall(byte[] data) {
+        Parcel p = Parcel.obtain();
+        p.unmarshall(data, 0, data.length);
+        p.setDataPosition(0);
+        return LowpanChannelInfo.CREATOR.createFromParcel(p);
+    }
+}
diff --git a/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanCredentialTest.java b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanCredentialTest.java
new file mode 100644
index 0000000..26e3334
--- /dev/null
+++ b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanCredentialTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.lowpan;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertArrayEquals;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LowpanCredentialTest {
+
+    static {
+        System.loadLibrary("frameworkslowpantestsjni");
+    }
+
+    private static native byte[] readAndWriteNative(byte[] inParcel);
+
+    public void testNativeParcelUnparcel(LowpanCredential original) {
+        byte[] inParcel = marshall(original);
+        byte[] outParcel = readAndWriteNative(inParcel);
+        LowpanCredential roundTrip = unmarshall(outParcel);
+
+        assertEquals(original, roundTrip);
+        assertArrayEquals(inParcel, outParcel);
+    }
+
+    @Test
+    public void testNativeParcelUnparcel() {
+        testNativeParcelUnparcel(
+                LowpanCredential.createMasterKey(
+                        new byte[] {
+                            (byte) 0x88,
+                            (byte) 0x99,
+                            (byte) 0xaa,
+                            (byte) 0xbb,
+                            (byte) 0xcc,
+                            (byte) 0xdd,
+                            (byte) 0xee,
+                            (byte) 0xff
+                        }));
+        testNativeParcelUnparcel(
+                LowpanCredential.createMasterKey(
+                        new byte[] {
+                            (byte) 0x88, (byte) 0x99, (byte) 0xaa, (byte) 0xbb, (byte) 0xcc
+                        },
+                        15));
+    }
+
+    /**
+     * Write a {@link LowpanCredential} into an empty parcel and return the underlying data.
+     *
+     * @see unmarshall(byte[])
+     */
+    private static byte[] marshall(LowpanCredential addr) {
+        Parcel p = Parcel.obtain();
+        addr.writeToParcel(p, /* flags */ 0);
+        p.setDataPosition(0);
+        return p.marshall();
+    }
+
+    /**
+     * Read raw bytes into a parcel, and read a {@link LowpanCredential} back out of them.
+     *
+     * @see marshall(LowpanCredential)
+     */
+    private static LowpanCredential unmarshall(byte[] data) {
+        Parcel p = Parcel.obtain();
+        p.unmarshall(data, 0, data.length);
+        p.setDataPosition(0);
+        return LowpanCredential.CREATOR.createFromParcel(p);
+    }
+}
diff --git a/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanIdentityTest.java b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanIdentityTest.java
new file mode 100644
index 0000000..1242e55
--- /dev/null
+++ b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanIdentityTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.lowpan;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LowpanIdentityTest {
+
+    static {
+        System.loadLibrary("frameworkslowpantestsjni");
+    }
+
+    private static native byte[] readAndWriteNative(byte[] inParcel);
+
+    public void testNativeParcelUnparcel(LowpanIdentity original) {
+        byte[] inParcel = marshall(original);
+        byte[] outParcel = readAndWriteNative(inParcel);
+        LowpanIdentity roundTrip = unmarshall(outParcel);
+
+        assertEquals(original, roundTrip);
+        assertEquals(original.hashCode(), roundTrip.hashCode());
+        assertEquals(original.getName(), roundTrip.getName());
+        assertArrayEquals(inParcel, outParcel);
+    }
+
+    @Test
+    public void testNativeParcelUnparcel1() {
+        testNativeParcelUnparcel(
+                new LowpanIdentity.Builder()
+                        .setName("TestNet1")
+                        .setPanid(0x1234)
+                        .setXpanid(
+                                new byte[] {
+                                    (byte) 0x00,
+                                    (byte) 0x11,
+                                    (byte) 0x22,
+                                    (byte) 0x33,
+                                    (byte) 0x44,
+                                    (byte) 0x55,
+                                    (byte) 0x66,
+                                    (byte) 0x77
+                                })
+                        .setType(LowpanInterface.NETWORK_TYPE_THREAD_V1)
+                        .setChannel(15)
+                        .build());
+    }
+
+    @Test
+    public void testNativeParcelUnparcel2() {
+        testNativeParcelUnparcel(
+                new LowpanIdentity.Builder()
+                        .setName("TestNet2")
+                        .setPanid(0x5678)
+                        .setXpanid(
+                                new byte[] {
+                                    (byte) 0x88,
+                                    (byte) 0x99,
+                                    (byte) 0xaa,
+                                    (byte) 0xbb,
+                                    (byte) 0xcc,
+                                    (byte) 0xdd,
+                                    (byte) 0xee,
+                                    (byte) 0xff
+                                })
+                        .setType("bork-bork-bork")
+                        .setChannel(16)
+                        .build());
+    }
+
+    @Test
+    public void testNativeParcelUnparcel3() {
+        testNativeParcelUnparcel(new LowpanIdentity.Builder().setName("TestNet3").build());
+    }
+
+    @Test
+    public void testNativeParcelUnparcel4() {
+        testNativeParcelUnparcel(new LowpanIdentity.Builder().build());
+    }
+
+    @Test
+    public void testNativeParcelUnparcel5() {
+        testNativeParcelUnparcel(
+                new LowpanIdentity.Builder()
+                        .setRawName(
+                                new byte[] {
+                                    (byte) 0x66,
+                                    (byte) 0x6F,
+                                    (byte) 0x6F,
+                                    (byte) 0xC2,
+                                    (byte) 0xAD,
+                                    (byte) 0xCD,
+                                    (byte) 0x8F,
+                                    (byte) 0xE1,
+                                    (byte) 0xA0,
+                                    (byte) 0x86,
+                                    (byte) 0xE1,
+                                    (byte) 0xA0,
+                                    (byte) 0x8B
+                                })
+                        .build());
+    }
+
+    @Test
+    public void testStringPrep1() {
+        LowpanIdentity identity =
+                new LowpanIdentity.Builder()
+                        .setRawName(
+                                new byte[] {
+                                    (byte) 0x66,
+                                    (byte) 0x6F,
+                                    (byte) 0x6F,
+                                    (byte) 0x20,
+                                    (byte) 0xC2,
+                                    (byte) 0xAD,
+                                    (byte) 0xCD,
+                                    (byte) 0x8F,
+                                    (byte) 0xE1,
+                                    (byte) 0xA0,
+                                    (byte) 0x86,
+                                    (byte) 0xE1,
+                                    (byte) 0xA0,
+                                    (byte) 0x8B
+                                })
+                        .build();
+
+        assertFalse(identity.isNameValid());
+    }
+
+    @Test
+    public void testStringPrep2() {
+        LowpanIdentity identity =
+                new LowpanIdentity.Builder()
+                        .setRawName(
+                                new byte[] {
+                                    (byte) 0x66, (byte) 0x6F, (byte) 0x6F, (byte) 0x20, (byte) 0x6F
+                                })
+                        .build();
+
+        assertEquals("foo o", identity.getName());
+        assertTrue(identity.isNameValid());
+    }
+
+    @Test
+    public void testStringPrep3() {
+        LowpanIdentity identity = new LowpanIdentity.Builder().setName("foo o").build();
+
+        assertTrue(identity.isNameValid());
+        assertEquals("foo o", identity.getName());
+    }
+
+    /**
+     * Write a {@link LowpanIdentity} into an empty parcel and return the underlying data.
+     *
+     * @see unmarshall(byte[])
+     */
+    private static byte[] marshall(LowpanIdentity addr) {
+        Parcel p = Parcel.obtain();
+        addr.writeToParcel(p, /* flags */ 0);
+        p.setDataPosition(0);
+        return p.marshall();
+    }
+
+    /**
+     * Read raw bytes into a parcel, and read a {@link LowpanIdentity} back out of them.
+     *
+     * @see marshall(LowpanIdentity)
+     */
+    private static LowpanIdentity unmarshall(byte[] data) {
+        Parcel p = Parcel.obtain();
+        p.unmarshall(data, 0, data.length);
+        p.setDataPosition(0);
+        return LowpanIdentity.CREATOR.createFromParcel(p);
+    }
+}
diff --git a/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanProvisionTest.java b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanProvisionTest.java
new file mode 100644
index 0000000..3be2f26
--- /dev/null
+++ b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanProvisionTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.lowpan;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertArrayEquals;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LowpanProvisionTest {
+
+    static {
+        System.loadLibrary("frameworkslowpantestsjni");
+    }
+
+    private static native byte[] readAndWriteNative(byte[] inParcel);
+
+    public void testNativeParcelUnparcel(LowpanProvision original) {
+        byte[] inParcel = marshall(original);
+        byte[] outParcel = readAndWriteNative(inParcel);
+        LowpanProvision roundTrip = unmarshall(outParcel);
+
+        assertEquals(original, roundTrip);
+        assertArrayEquals(inParcel, outParcel);
+    }
+
+    @Test
+    public void testNativeParcelUnparcel() {
+        testNativeParcelUnparcel(
+                new LowpanProvision.Builder()
+                        .setLowpanIdentity(
+                                new LowpanIdentity.Builder()
+                                        .setName("TestNet1")
+                                        .setPanid(0x1234)
+                                        .setXpanid(
+                                                new byte[] {
+                                                    (byte) 0x00,
+                                                    (byte) 0x11,
+                                                    (byte) 0x22,
+                                                    (byte) 0x33,
+                                                    (byte) 0x44,
+                                                    (byte) 0x55,
+                                                    (byte) 0x66,
+                                                    (byte) 0x77
+                                                })
+                                        .setType(LowpanInterface.NETWORK_TYPE_THREAD_V1)
+                                        .setChannel(15)
+                                        .build())
+                        .build());
+        testNativeParcelUnparcel(
+                new LowpanProvision.Builder()
+                        .setLowpanIdentity(
+                                new LowpanIdentity.Builder()
+                                        .setName("TestNet2")
+                                        .setPanid(0x5678)
+                                        .setXpanid(
+                                                new byte[] {
+                                                    (byte) 0x88,
+                                                    (byte) 0x99,
+                                                    (byte) 0xaa,
+                                                    (byte) 0xbb,
+                                                    (byte) 0xcc,
+                                                    (byte) 0xdd,
+                                                    (byte) 0xee,
+                                                    (byte) 0xff
+                                                })
+                                        .setType("bork-bork-bork")
+                                        .setChannel(16)
+                                        .build())
+                        .setLowpanCredential(
+                                LowpanCredential.createMasterKey(
+                                        new byte[] {
+                                            (byte) 0x88,
+                                            (byte) 0x99,
+                                            (byte) 0xaa,
+                                            (byte) 0xbb,
+                                            (byte) 0xcc,
+                                            (byte) 0xdd,
+                                            (byte) 0xee,
+                                            (byte) 0xff
+                                        }))
+                        .build());
+    }
+
+    /**
+     * Write a {@link LowpanProvision} into an empty parcel and return the underlying data.
+     *
+     * @see unmarshall(byte[])
+     */
+    private static byte[] marshall(LowpanProvision addr) {
+        Parcel p = Parcel.obtain();
+        addr.writeToParcel(p, /* flags */ 0);
+        p.setDataPosition(0);
+        return p.marshall();
+    }
+
+    /**
+     * Read raw bytes into a parcel, and read a {@link LowpanProvision} back out of them.
+     *
+     * @see marshall(LowpanProvision)
+     */
+    private static LowpanProvision unmarshall(byte[] data) {
+        Parcel p = Parcel.obtain();
+        p.unmarshall(data, 0, data.length);
+        p.setDataPosition(0);
+        return LowpanProvision.CREATOR.createFromParcel(p);
+    }
+}
diff --git a/libandroid_net_lowpan/tests/jni/LowpanBeaconInfoTest.cpp b/libandroid_net_lowpan/tests/jni/LowpanBeaconInfoTest.cpp
new file mode 100644
index 0000000..efc5f18
--- /dev/null
+++ b/libandroid_net_lowpan/tests/jni/LowpanBeaconInfoTest.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include <binder/Parcel.h>
+
+#include "LowpanBeaconInfoTest.h"
+
+using android::net::lowpan::LowpanBeaconInfo;
+
+/**
+ * Reads exactly one LowpanBeaconInfo from 'parcelData' assuming that it is a Parcel. Any bytes afterward
+ * are ignored.
+ */
+static LowpanBeaconInfo unmarshall(JNIEnv* env, jbyteArray parcelData) {
+    const int length = env->GetArrayLength(parcelData);
+
+    std::unique_ptr<uint8_t> bytes(new uint8_t[length]);
+    env->GetByteArrayRegion(parcelData, 0, length, reinterpret_cast<jbyte*>(bytes.get()));
+
+    android::Parcel p;
+    p.setData(bytes.get(), length);
+
+    LowpanBeaconInfo value;
+    value.readFromParcel(&p);
+    return value;
+}
+
+/**
+ * Creates a Java byte[] array and writes the contents of 'addr' to it as a Parcel containing
+ * exactly one object.
+ *
+ * Every LowpanBeaconInfo maps to a unique parcel object, so both 'marshall(e, unmarshall(e, x))' and
+ * 'unmarshall(e, marshall(e, x))' should be fixed points.
+ */
+static jbyteArray marshall(JNIEnv* env, const LowpanBeaconInfo& addr) {
+    android::Parcel p;
+    addr.writeToParcel(&p);
+    const int length = p.dataSize();
+
+    jbyteArray parcelData = env->NewByteArray(length);
+    env->SetByteArrayRegion(parcelData, 0, length, reinterpret_cast<const jbyte*>(p.data()));
+
+    return parcelData;
+}
+
+extern "C"
+JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanBeaconInfoTest_readAndWriteNative(JNIEnv* env, jclass,
+        jbyteArray inParcel) {
+    const LowpanBeaconInfo value = unmarshall(env, inParcel);
+    return marshall(env, value);
+}
diff --git a/libandroid_net_lowpan/tests/jni/LowpanBeaconInfoTest.h b/libandroid_net_lowpan/tests/jni/LowpanBeaconInfoTest.h
new file mode 100644
index 0000000..1ba8eaf
--- /dev/null
+++ b/libandroid_net_lowpan/tests/jni/LowpanBeaconInfoTest.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_NET_LOWPANBEACONINFOTEST_H_
+#define _ANDROID_NET_LOWPANBEACONINFOTEST_H_
+
+#include <jni.h>
+#include <android/net/lowpan/LowpanBeaconInfo.h>
+
+extern "C"
+JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanBeaconInfoTest_readAndWriteNative(JNIEnv* env, jclass,
+        jbyteArray inParcel);
+
+#endif  //  _ANDROID_NET_LOWPANBEACONINFOTEST_H_
diff --git a/libandroid_net_lowpan/tests/jni/LowpanChannelInfoTest.cpp b/libandroid_net_lowpan/tests/jni/LowpanChannelInfoTest.cpp
new file mode 100644
index 0000000..03bb72a
--- /dev/null
+++ b/libandroid_net_lowpan/tests/jni/LowpanChannelInfoTest.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include <binder/Parcel.h>
+
+#include "LowpanChannelInfoTest.h"
+
+using android::net::lowpan::LowpanChannelInfo;
+
+/**
+ * Reads exactly one LowpanChannelInfo from 'parcelData' assuming that it is a Parcel. Any bytes afterward
+ * are ignored.
+ */
+static LowpanChannelInfo unmarshall(JNIEnv* env, jbyteArray parcelData) {
+    const int length = env->GetArrayLength(parcelData);
+
+    std::unique_ptr<uint8_t> bytes(new uint8_t[length]);
+    env->GetByteArrayRegion(parcelData, 0, length, reinterpret_cast<jbyte*>(bytes.get()));
+
+    android::Parcel p;
+    p.setData(bytes.get(), length);
+
+    LowpanChannelInfo value;
+    value.readFromParcel(&p);
+    return value;
+}
+
+/**
+ * Creates a Java byte[] array and writes the contents of 'addr' to it as a Parcel containing
+ * exactly one object.
+ *
+ * Every LowpanChannelInfo maps to a unique parcel object, so both 'marshall(e, unmarshall(e, x))' and
+ * 'unmarshall(e, marshall(e, x))' should be fixed points.
+ */
+static jbyteArray marshall(JNIEnv* env, const LowpanChannelInfo& addr) {
+    android::Parcel p;
+    addr.writeToParcel(&p);
+    const int length = p.dataSize();
+
+    jbyteArray parcelData = env->NewByteArray(length);
+    env->SetByteArrayRegion(parcelData, 0, length, reinterpret_cast<const jbyte*>(p.data()));
+
+    return parcelData;
+}
+
+extern "C"
+JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanChannelInfoTest_readAndWriteNative(JNIEnv* env, jclass,
+        jbyteArray inParcel) {
+    const LowpanChannelInfo value = unmarshall(env, inParcel);
+    return marshall(env, value);
+}
diff --git a/libandroid_net_lowpan/tests/jni/LowpanChannelInfoTest.h b/libandroid_net_lowpan/tests/jni/LowpanChannelInfoTest.h
new file mode 100644
index 0000000..3b29a90
--- /dev/null
+++ b/libandroid_net_lowpan/tests/jni/LowpanChannelInfoTest.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_NET_LOWPANCHANNELINFOTEST_H_
+#define _ANDROID_NET_LOWPANCHANNELINFOTEST_H_
+
+#include <jni.h>
+#include <android/net/lowpan/LowpanChannelInfo.h>
+
+extern "C"
+JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanChannelInfoTest_readAndWriteNative(JNIEnv* env, jclass,
+        jbyteArray inParcel);
+
+#endif  //  _ANDROID_NET_LOWPANCHANNELINFOTEST_H_
diff --git a/libandroid_net_lowpan/tests/jni/LowpanCredentialTest.cpp b/libandroid_net_lowpan/tests/jni/LowpanCredentialTest.cpp
new file mode 100644
index 0000000..fc860b2
--- /dev/null
+++ b/libandroid_net_lowpan/tests/jni/LowpanCredentialTest.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include <binder/Parcel.h>
+
+#include "LowpanCredentialTest.h"
+
+using android::net::lowpan::LowpanCredential;
+
+/**
+ * Reads exactly one LowpanCredential from 'parcelData' assuming that it is a Parcel. Any bytes afterward
+ * are ignored.
+ */
+static LowpanCredential unmarshall(JNIEnv* env, jbyteArray parcelData) {
+    const int length = env->GetArrayLength(parcelData);
+
+    std::unique_ptr<uint8_t> bytes(new uint8_t[length]);
+    env->GetByteArrayRegion(parcelData, 0, length, reinterpret_cast<jbyte*>(bytes.get()));
+
+    android::Parcel p;
+    p.setData(bytes.get(), length);
+
+    LowpanCredential value;
+    value.readFromParcel(&p);
+    return value;
+}
+
+/**
+ * Creates a Java byte[] array and writes the contents of 'addr' to it as a Parcel containing
+ * exactly one object.
+ *
+ * Every LowpanCredential maps to a unique parcel object, so both 'marshall(e, unmarshall(e, x))' and
+ * 'unmarshall(e, marshall(e, x))' should be fixed points.
+ */
+static jbyteArray marshall(JNIEnv* env, const LowpanCredential& addr) {
+    android::Parcel p;
+    addr.writeToParcel(&p);
+    const int length = p.dataSize();
+
+    jbyteArray parcelData = env->NewByteArray(length);
+    env->SetByteArrayRegion(parcelData, 0, length, reinterpret_cast<const jbyte*>(p.data()));
+
+    return parcelData;
+}
+
+extern "C"
+JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanCredentialTest_readAndWriteNative(JNIEnv* env, jclass,
+        jbyteArray inParcel) {
+    const LowpanCredential value = unmarshall(env, inParcel);
+    return marshall(env, value);
+}
diff --git a/libandroid_net_lowpan/tests/jni/LowpanCredentialTest.h b/libandroid_net_lowpan/tests/jni/LowpanCredentialTest.h
new file mode 100644
index 0000000..9dd9889
--- /dev/null
+++ b/libandroid_net_lowpan/tests/jni/LowpanCredentialTest.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_NET_LOWPANCREDENTIALTEST_H_
+#define _ANDROID_NET_LOWPANCREDENTIALTEST_H_
+
+#include <jni.h>
+#include <android/net/lowpan/LowpanCredential.h>
+
+extern "C"
+JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanCredentialTest_readAndWriteNative(JNIEnv* env, jclass,
+        jbyteArray inParcel);
+
+#endif  //  _ANDROID_NET_LOWPANCREDENTIALTEST_H_
diff --git a/libandroid_net_lowpan/tests/jni/LowpanIdentityTest.cpp b/libandroid_net_lowpan/tests/jni/LowpanIdentityTest.cpp
new file mode 100644
index 0000000..1a9ad33
--- /dev/null
+++ b/libandroid_net_lowpan/tests/jni/LowpanIdentityTest.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include <binder/Parcel.h>
+
+#include "LowpanIdentityTest.h"
+
+using android::net::lowpan::LowpanIdentity;
+
+/**
+ * Reads exactly one LowpanIdentity from 'parcelData' assuming that it is a Parcel. Any bytes afterward
+ * are ignored.
+ */
+static LowpanIdentity unmarshall(JNIEnv* env, jbyteArray parcelData) {
+    const int length = env->GetArrayLength(parcelData);
+
+    std::unique_ptr<uint8_t> bytes(new uint8_t[length]);
+    env->GetByteArrayRegion(parcelData, 0, length, reinterpret_cast<jbyte*>(bytes.get()));
+
+    android::Parcel p;
+    p.setData(bytes.get(), length);
+
+    LowpanIdentity value;
+    value.readFromParcel(&p);
+    return value;
+}
+
+/**
+ * Creates a Java byte[] array and writes the contents of 'addr' to it as a Parcel containing
+ * exactly one object.
+ *
+ * Every LowpanIdentity maps to a unique parcel object, so both 'marshall(e, unmarshall(e, x))' and
+ * 'unmarshall(e, marshall(e, x))' should be fixed points.
+ */
+static jbyteArray marshall(JNIEnv* env, const LowpanIdentity& addr) {
+    android::Parcel p;
+    addr.writeToParcel(&p);
+    const int length = p.dataSize();
+
+    jbyteArray parcelData = env->NewByteArray(length);
+    env->SetByteArrayRegion(parcelData, 0, length, reinterpret_cast<const jbyte*>(p.data()));
+
+    return parcelData;
+}
+
+extern "C"
+JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanIdentityTest_readAndWriteNative(JNIEnv* env, jclass,
+        jbyteArray inParcel) {
+    const LowpanIdentity value = unmarshall(env, inParcel);
+    return marshall(env, value);
+}
diff --git a/libandroid_net_lowpan/tests/jni/LowpanIdentityTest.h b/libandroid_net_lowpan/tests/jni/LowpanIdentityTest.h
new file mode 100644
index 0000000..1d2c465
--- /dev/null
+++ b/libandroid_net_lowpan/tests/jni/LowpanIdentityTest.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_NET_LOWPANIDENTITYTEST_H_
+#define _ANDROID_NET_LOWPANIDENTITYTEST_H_
+
+#include <jni.h>
+#include <android/net/lowpan/LowpanIdentity.h>
+
+extern "C"
+JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanIdentityTest_readAndWriteNative(JNIEnv* env, jclass,
+        jbyteArray inParcel);
+
+#endif  //  _ANDROID_NET_LOWPANIDENTITYTEST_H_
diff --git a/libandroid_net_lowpan/tests/jni/LowpanProvisionTest.cpp b/libandroid_net_lowpan/tests/jni/LowpanProvisionTest.cpp
new file mode 100644
index 0000000..95f64b6
--- /dev/null
+++ b/libandroid_net_lowpan/tests/jni/LowpanProvisionTest.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include <binder/Parcel.h>
+
+#include "LowpanProvisionTest.h"
+
+using android::net::lowpan::LowpanProvision;
+
+/**
+ * Reads exactly one LowpanProvision from 'parcelData' assuming that it is a Parcel. Any bytes afterward
+ * are ignored.
+ */
+static LowpanProvision unmarshall(JNIEnv* env, jbyteArray parcelData) {
+    const int length = env->GetArrayLength(parcelData);
+
+    std::unique_ptr<uint8_t> bytes(new uint8_t[length]);
+    env->GetByteArrayRegion(parcelData, 0, length, reinterpret_cast<jbyte*>(bytes.get()));
+
+    android::Parcel p;
+    p.setData(bytes.get(), length);
+
+    LowpanProvision value;
+    value.readFromParcel(&p);
+    return value;
+}
+
+/**
+ * Creates a Java byte[] array and writes the contents of 'addr' to it as a Parcel containing
+ * exactly one object.
+ *
+ * Every LowpanProvision maps to a unique parcel object, so both 'marshall(e, unmarshall(e, x))' and
+ * 'unmarshall(e, marshall(e, x))' should be fixed points.
+ */
+static jbyteArray marshall(JNIEnv* env, const LowpanProvision& addr) {
+    android::Parcel p;
+    addr.writeToParcel(&p);
+    const int length = p.dataSize();
+
+    jbyteArray parcelData = env->NewByteArray(length);
+    env->SetByteArrayRegion(parcelData, 0, length, reinterpret_cast<const jbyte*>(p.data()));
+
+    return parcelData;
+}
+
+extern "C"
+JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanProvisionTest_readAndWriteNative(JNIEnv* env, jclass,
+        jbyteArray inParcel) {
+    const LowpanProvision value = unmarshall(env, inParcel);
+    return marshall(env, value);
+}
diff --git a/libandroid_net_lowpan/tests/jni/LowpanProvisionTest.h b/libandroid_net_lowpan/tests/jni/LowpanProvisionTest.h
new file mode 100644
index 0000000..49211b5
--- /dev/null
+++ b/libandroid_net_lowpan/tests/jni/LowpanProvisionTest.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_NET_LOWPANPROVISIONTEST_H_
+#define _ANDROID_NET_LOWPANPROVISIONTEST_H_
+
+#include <jni.h>
+#include <android/net/lowpan/LowpanProvision.h>
+
+extern "C"
+JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanProvisionTest_readAndWriteNative(JNIEnv* env, jclass,
+        jbyteArray inParcel);
+
+#endif  //  _ANDROID_NET_LOWPANPROVISIONTEST_H_
diff --git a/libandroid_net_lowpan/tests/runtests.sh b/libandroid_net_lowpan/tests/runtests.sh
new file mode 100755
index 0000000..c3cc4da
--- /dev/null
+++ b/libandroid_net_lowpan/tests/runtests.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+if [ -z $ANDROID_BUILD_TOP ]; then
+  echo "You need to source and lunch before you can use this script"
+  exit 1
+fi
+
+echo "Running tests"
+
+set -e # fail early
+
+echo "+ mmma -j32 $ANDROID_BUILD_TOP/frameworks/opt/net/lowpan/libandroid_net_lowpan/tests"
+# NOTE Don't actually run the command above since this shell doesn't inherit functions from the
+#      caller.
+make -j32 -C $ANDROID_BUILD_TOP -f build/core/main.mk MODULES-IN-frameworks-opt-net-lowpan-libandroid_net_lowpan-tests
+
+set -x # print commands
+
+adb root
+adb wait-for-device
+
+adb install -r -g "$OUT/data/app/FrameworksLowpanApiNativeTests/FrameworksLowpanApiNativeTests.apk"
+
+adb shell am instrument -w "$@" 'android.net.lowpan.testnative/android.support.test.runner.AndroidJUnitRunner'