Merge "Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls."
diff --git a/init/init.c b/init/init.c
index 365726c..ab52749 100644
--- a/init/init.c
+++ b/init/init.c
@@ -1132,7 +1132,7 @@
             continue;
 
         for (i = 0; i < fd_count; i++) {
-            if (ufds[i].revents == POLLIN) {
+            if (ufds[i].revents & POLLIN) {
                 if (ufds[i].fd == get_property_set_fd())
                     handle_property_set_fd();
                 else if (ufds[i].fd == get_keychord_fd())
diff --git a/init/ueventd.c b/init/ueventd.c
index a41c31e..3d01836 100644
--- a/init/ueventd.c
+++ b/init/ueventd.c
@@ -94,7 +94,7 @@
         nr = poll(&ufd, 1, -1);
         if (nr <= 0)
             continue;
-        if (ufd.revents == POLLIN)
+        if (ufd.revents & POLLIN)
                handle_device_fd();
     }
 }
diff --git a/libion/Android.mk b/libion/Android.mk
index 42e6f07..e5d495b 100644
--- a/libion/Android.mk
+++ b/libion/Android.mk
@@ -16,3 +16,5 @@
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers
 LOCAL_SHARED_LIBRARIES := liblog
 include $(BUILD_EXECUTABLE)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/libion/ion_test.c b/libion/ion_test.c
index 95e020a..12163e9 100644
--- a/libion/ion_test.c
+++ b/libion/ion_test.c
@@ -1,3 +1,19 @@
+/*
+ *   Copyright 2013 Google, Inc
+ *
+ *  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 <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
diff --git a/libion/kernel-headers/linux/ion_test.h b/libion/kernel-headers/linux/ion_test.h
index 2b0f062..6f3e2a7 100644
--- a/libion/kernel-headers/linux/ion_test.h
+++ b/libion/kernel-headers/linux/ion_test.h
@@ -27,10 +27,12 @@
  __u64 size;
 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
  int write;
+ int __padding;
 };
 #define ION_IOC_MAGIC 'I'
-#define ION_IOC_TEST_SET_FD   _IO(ION_IOC_MAGIC, 0xf0)
 /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ION_IOC_TEST_SET_FD   _IO(ION_IOC_MAGIC, 0xf0)
 #define ION_IOC_TEST_DMA_MAPPING   _IOW(ION_IOC_MAGIC, 0xf1, struct ion_test_rw_data)
 #define ION_IOC_TEST_KERNEL_MAPPING   _IOW(ION_IOC_MAGIC, 0xf2, struct ion_test_rw_data)
 #endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
diff --git a/libion/original-kernel-headers/linux/ion_test.h b/libion/original-kernel-headers/linux/ion_test.h
index 614d1e3..ffef06f 100644
--- a/libion/original-kernel-headers/linux/ion_test.h
+++ b/libion/original-kernel-headers/linux/ion_test.h
@@ -32,6 +32,7 @@
 	__u64 offset;
 	__u64 size;
 	int write;
+	int __padding;
 };
 
 #define ION_IOC_MAGIC		'I'
diff --git a/libion/tests/Android.mk b/libion/tests/Android.mk
new file mode 100644
index 0000000..8dc7f9d
--- /dev/null
+++ b/libion/tests/Android.mk
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2013 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)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ion-unit-tests
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_SHARED_LIBRARIES += libion
+LOCAL_STATIC_LIBRARIES += libgtest_main
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../kernel-headers
+LOCAL_SRC_FILES := \
+	ion_test_fixture.cpp \
+	allocate_test.cpp \
+	formerly_valid_handle_test.cpp \
+	invalid_values_test.cpp \
+	map_test.cpp \
+	device_test.cpp \
+	exit_test.cpp
+include $(BUILD_NATIVE_TEST)
diff --git a/libion/tests/allocate_test.cpp b/libion/tests/allocate_test.cpp
new file mode 100644
index 0000000..e26b302
--- /dev/null
+++ b/libion/tests/allocate_test.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2013 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 <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+#include "ion_test_fixture.h"
+
+class Allocate : public IonAllHeapsTest {
+};
+
+TEST_F(Allocate, Allocate)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            ion_user_handle_t handle = 0;
+            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+            ASSERT_TRUE(handle != 0);
+            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+        }
+    }
+}
+
+TEST_F(Allocate, AllocateCached)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            ion_user_handle_t handle = 0;
+            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &handle));
+            ASSERT_TRUE(handle != 0);
+            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+        }
+    }
+}
+
+TEST_F(Allocate, AllocateCachedNeedsSync)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            ion_user_handle_t handle = 0;
+            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED_NEEDS_SYNC, &handle));
+            ASSERT_TRUE(handle != 0);
+            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+        }
+    }
+}
+
+TEST_F(Allocate, RepeatedAllocate)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            ion_user_handle_t handle = 0;
+
+            for (unsigned int i = 0; i < 1024; i++) {
+                SCOPED_TRACE(::testing::Message() << "iteration " << i);
+                ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+                ASSERT_TRUE(handle != 0);
+                ASSERT_EQ(0, ion_free(m_ionFd, handle));
+            }
+        }
+    }
+}
+
+TEST_F(Allocate, Zeroed)
+{
+    void *zeroes = calloc(4096, 1);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int fds[16];
+        for (unsigned int i = 0; i < 16; i++) {
+            int map_fd = -1;
+
+            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, &map_fd));
+            ASSERT_GE(map_fd, 0);
+
+            void *ptr = NULL;
+            ptr = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, map_fd, 0);
+            ASSERT_TRUE(ptr != NULL);
+
+            memset(ptr, 0xaa, 4096);
+
+            ASSERT_EQ(0, munmap(ptr, 4096));
+            fds[i] = map_fd;
+        }
+
+        for (unsigned int i = 0; i < 16; i++) {
+            ASSERT_EQ(0, close(fds[i]));
+        }
+
+        int newIonFd = ion_open();
+        int map_fd = -1;
+
+        ASSERT_EQ(0, ion_alloc_fd(newIonFd, 4096, 0, heapMask, 0, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr = NULL;
+        ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        ASSERT_EQ(0, memcmp(ptr, zeroes, 4096));
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(zeroes);
+
+}
+
+TEST_F(Allocate, Large)
+{
+    for (unsigned int heapMask : m_allHeaps) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        ion_user_handle_t handle = 0;
+        ASSERT_EQ(-ENOMEM, ion_alloc(m_ionFd, 3UL*1024*1024*1024, 0, heapMask, 0, &handle));
+    }
+}
diff --git a/libion/tests/device_test.cpp b/libion/tests/device_test.cpp
new file mode 100644
index 0000000..6f6e1bd
--- /dev/null
+++ b/libion/tests/device_test.cpp
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2013 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 <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <linux/ion_test.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+#define ALIGN(x,y) (((x) + ((y) - 1)) & ~((y) - 1))
+
+class Device : public IonAllHeapsTest {
+ public:
+    virtual void SetUp();
+    virtual void TearDown();
+    int m_deviceFd;
+    void readDMA(int fd, void *buf, size_t size);
+    void writeDMA(int fd, void *buf, size_t size);
+    void readKernel(int fd, void *buf, size_t size);
+    void writeKernel(int fd, void *buf, size_t size);
+    void blowCache();
+    void dirtyCache(void *ptr, size_t size);
+};
+
+void Device::SetUp()
+{
+    IonAllHeapsTest::SetUp();
+    m_deviceFd = open("/dev/ion-test", O_RDWR);
+    ASSERT_GE(m_deviceFd, 0);
+}
+
+void Device::TearDown()
+{
+    ASSERT_EQ(0, close(m_deviceFd));
+    IonAllHeapsTest::TearDown();
+}
+
+void Device::readDMA(int fd, void *buf, size_t size)
+{
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+    struct ion_test_rw_data ion_test_rw_data = {
+            .ptr = (uint64_t)buf,
+            .offset = 0,
+            .size = size,
+            .write = 0,
+    };
+
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::writeDMA(int fd, void *buf, size_t size)
+{
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+    struct ion_test_rw_data ion_test_rw_data = {
+            .ptr = (uint64_t)buf,
+            .offset = 0,
+            .size = size,
+            .write = 1,
+    };
+
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::readKernel(int fd, void *buf, size_t size)
+{
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+    struct ion_test_rw_data ion_test_rw_data = {
+            .ptr = (uint64_t)buf,
+            .offset = 0,
+            .size = size,
+            .write = 0,
+    };
+
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::writeKernel(int fd, void *buf, size_t size)
+{
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+    struct ion_test_rw_data ion_test_rw_data = {
+            .ptr = (uint64_t)buf,
+            .offset = 0,
+            .size = size,
+            .write = 1,
+    };
+
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::blowCache()
+{
+    const size_t bigger_than_cache = 8*1024*1024;
+    void *buf1 = malloc(bigger_than_cache);
+    void *buf2 = malloc(bigger_than_cache);
+    memset(buf1, 0xaa, bigger_than_cache);
+    memcpy(buf2, buf1, bigger_than_cache);
+    free(buf1);
+    free(buf2);
+}
+
+void Device::dirtyCache(void *ptr, size_t size)
+{
+    /* try to dirty cache lines */
+    for (size_t i = size-1; i > 0; i--) {
+        ((volatile char *)ptr)[i];
+        ((char *)ptr)[i] = i;
+    }
+}
+
+TEST_F(Device, KernelReadCached)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        ((char*)buf)[4096] = 0x12;
+        readKernel(map_fd, buf, 4096);
+        ASSERT_EQ(((char*)buf)[4096], 0x12);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, KernelWriteCached)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeKernel(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, DMAReadCached)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        readDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, DMAWriteCached)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, KernelReadCachedNeedsSync)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        ((char*)buf)[4096] = 0x12;
+        readKernel(map_fd, buf, 4096);
+        ASSERT_EQ(((char*)buf)[4096], 0x12);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, KernelWriteCachedNeedsSync)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeKernel(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, DMAReadCachedNeedsSync)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        ion_sync_fd(m_ionFd, map_fd);
+
+        readDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, DMAWriteCachedNeedsSync)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeDMA(map_fd, buf, 4096);
+
+        ion_sync_fd(m_ionFd, map_fd);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+TEST_F(Device, KernelRead)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = 0;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        ((char*)buf)[4096] = 0x12;
+        readKernel(map_fd, buf, 4096);
+        ASSERT_EQ(((char*)buf)[4096], 0x12);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, KernelWrite)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = 0;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeKernel(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, DMARead)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = 0;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        readDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, DMAWrite)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = 0;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, IsCached)
+{
+    void *buf = malloc(4096);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        readDMA(map_fd, buf, 4096);
+
+        bool same = true;
+        for (int i = 4096-16; i >= 0; i -= 16)
+            if (((char *)buf)[i] != i)
+                same = false;
+        ASSERT_FALSE(same);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
diff --git a/libion/tests/exit_test.cpp b/libion/tests/exit_test.cpp
new file mode 100644
index 0000000..cdd3e27
--- /dev/null
+++ b/libion/tests/exit_test.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2013 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 <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+class Exit : public IonAllHeapsTest {
+};
+
+TEST_F(Exit, WithAlloc)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                ion_user_handle_t handle = 0;
+
+                ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+                ASSERT_TRUE(handle != 0);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
+
+TEST_F(Exit, WithAllocFd)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int handle_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
+                ASSERT_NE(-1, handle_fd);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
+
+TEST_F(Exit, WithRepeatedAllocFd)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            for (unsigned int i = 0; i < 1024; i++) {
+                SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+                SCOPED_TRACE(::testing::Message() << "size " << size);
+                ASSERT_EXIT({
+                    int handle_fd = -1;
+
+                    ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
+                    ASSERT_NE(-1, handle_fd);
+                    exit(0);
+                }, ::testing::ExitedWithCode(0), "")
+                        << "failed on heap " << heapMask
+                        << " and size " << size
+                        << " on iteration " << i;
+            }
+        }
+    }
+}
+
+
+TEST_F(Exit, WithMapping)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+
+}
+
+TEST_F(Exit, WithPartialMapping)
+{
+    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+
+                ASSERT_EQ(0, munmap(ptr, size / 2));
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
+
+TEST_F(Exit, WithMappingCached)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+
+}
+
+TEST_F(Exit, WithPartialMappingCached)
+{
+    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+
+                ASSERT_EQ(0, munmap(ptr, size / 2));
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
+
+TEST_F(Exit, WithMappingNeedsSync)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+
+}
+
+TEST_F(Exit, WithPartialMappingNeedsSync)
+{
+    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+
+                ASSERT_EQ(0, munmap(ptr, size / 2));
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
diff --git a/libion/tests/formerly_valid_handle_test.cpp b/libion/tests/formerly_valid_handle_test.cpp
new file mode 100644
index 0000000..01ab8f3
--- /dev/null
+++ b/libion/tests/formerly_valid_handle_test.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 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 <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+class FormerlyValidHandle : public IonTest {
+ public:
+    virtual void SetUp();
+    virtual void TearDown();
+    ion_user_handle_t m_handle;
+};
+
+void FormerlyValidHandle::SetUp()
+{
+    IonTest::SetUp();
+    ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, 1/* ion_env->m_firstHeap */, 0, &m_handle));
+    ASSERT_TRUE(m_handle != 0);
+    ASSERT_EQ(0, ion_free(m_ionFd, m_handle));
+}
+
+void FormerlyValidHandle::TearDown()
+{
+    m_handle = 0;
+}
+
+TEST_F(FormerlyValidHandle, free)
+{
+	ASSERT_EQ(-EINVAL, ion_free(m_ionFd, m_handle));
+}
+
+TEST_F(FormerlyValidHandle, map)
+{
+    int map_fd;
+    unsigned char *ptr;
+
+    ASSERT_EQ(-EINVAL, ion_map(m_ionFd, m_handle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+}
+
+TEST_F(FormerlyValidHandle, share)
+{
+    int share_fd;
+
+    ASSERT_EQ(-EINVAL, ion_share(m_ionFd, m_handle, &share_fd));
+}
diff --git a/libion/tests/invalid_values_test.cpp b/libion/tests/invalid_values_test.cpp
new file mode 100644
index 0000000..77fea17
--- /dev/null
+++ b/libion/tests/invalid_values_test.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2013 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 <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+class InvalidValues : public IonAllHeapsTest {
+ public:
+    virtual void SetUp();
+    virtual void TearDown();
+    ion_user_handle_t m_validHandle;
+    int m_validShareFd;
+    ion_user_handle_t const m_badHandle = -1;
+};
+
+void InvalidValues::SetUp()
+{
+    IonAllHeapsTest::SetUp();
+    ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, m_firstHeap, 0, &m_validHandle))
+      << m_ionFd << " " << m_firstHeap;
+    ASSERT_TRUE(m_validHandle != 0);
+    ASSERT_EQ(0, ion_share(m_ionFd, m_validHandle, &m_validShareFd));
+}
+
+void InvalidValues::TearDown()
+{
+    ASSERT_EQ(0, ion_free(m_ionFd, m_validHandle));
+    ASSERT_EQ(0, close(m_validShareFd));
+    m_validHandle = 0;
+    IonAllHeapsTest::TearDown();
+}
+
+TEST_F(InvalidValues, ion_close)
+{
+    EXPECT_EQ(-EBADF, ion_close(-1));
+}
+
+TEST_F(InvalidValues, ion_alloc)
+{
+    ion_user_handle_t handle;
+    /* invalid ion_fd */
+    int ret = ion_alloc(0, 4096, 0, m_firstHeap, 0, &handle);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion_fd */
+    EXPECT_EQ(-EBADF, ion_alloc(-1, 4096, 0, m_firstHeap, 0, &handle));
+    /* no heaps */
+    EXPECT_EQ(-ENODEV, ion_alloc(m_ionFd, 4096, 0, 0, 0, &handle));
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        /* zero size */
+        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 0, 0, heapMask, 0, &handle));
+        /* too large size */
+        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, -1, 0, heapMask, 0, &handle));
+        /* bad alignment */
+        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, -1, heapMask, 0, &handle));
+        /* NULL handle */
+        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, 0, heapMask, 0, NULL));
+    }
+}
+
+TEST_F(InvalidValues, ion_alloc_fd)
+{
+    int fd;
+    /* invalid ion_fd */
+    int ret = ion_alloc_fd(0, 4096, 0, m_firstHeap, 0, &fd);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion_fd */
+    EXPECT_EQ(-EBADF, ion_alloc_fd(-1, 4096, 0, m_firstHeap, 0, &fd));
+    /* no heaps */
+    EXPECT_EQ(-ENODEV, ion_alloc_fd(m_ionFd, 4096, 0, 0, 0, &fd));
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        /* zero size */
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 0, 0, heapMask, 0, &fd));
+        /* too large size */
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, -1, 0, heapMask, 0, &fd));
+        /* bad alignment */
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, -1, heapMask, 0, &fd));
+        /* NULL handle */
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, NULL));
+    }
+}
+
+TEST_F(InvalidValues, ion_free)
+{
+    /* invalid ion fd */
+    int ret = ion_free(0, m_validHandle);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_free(-1, m_validHandle));
+    /* zero handle */
+    EXPECT_EQ(-EINVAL, ion_free(m_ionFd, 0));
+    /* bad handle */
+    EXPECT_EQ(-EINVAL, ion_free(m_ionFd, m_badHandle));
+}
+
+TEST_F(InvalidValues, ion_map)
+{
+    int map_fd;
+    unsigned char *ptr;
+
+    /* invalid ion fd */
+    int ret = ion_map(0, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_map(-1, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+    /* zero handle */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, 0, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+    /* bad handle */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_badHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+    /* zero length */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 0, PROT_READ, 0, 0, &ptr, &map_fd));
+    /* bad prot */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, -1, 0, 0, &ptr, &map_fd));
+    /* bad offset */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, -1, &ptr, &map_fd));
+    /* NULL ptr */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, NULL, &map_fd));
+    /* NULL map_fd */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, NULL));
+}
+
+TEST_F(InvalidValues, ion_share)
+{
+    int share_fd;
+
+    /* invalid ion fd */
+    int ret = ion_share(0, m_validHandle, &share_fd);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_share(-1, m_validHandle, &share_fd));
+    /* zero handle */
+    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, 0, &share_fd));
+    /* bad handle */
+    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_badHandle, &share_fd));
+    /* NULL share_fd */
+    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_validHandle, NULL));
+}
+
+TEST_F(InvalidValues, ion_import)
+{
+    ion_user_handle_t handle;
+
+    /* invalid ion fd */
+    int ret = ion_import(0, m_validShareFd, &handle);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_import(-1, m_validShareFd, &handle));
+    /* bad share_fd */
+    EXPECT_EQ(-EINVAL, ion_import(m_ionFd, 0, &handle));
+    /* invalid share_fd */
+    EXPECT_EQ(-EBADF, ion_import(m_ionFd, -1, &handle));
+    /* NULL handle */
+    EXPECT_EQ(-EINVAL, ion_import(m_ionFd, m_validShareFd, NULL));
+}
+
+TEST_F(InvalidValues, ion_sync_fd)
+{
+    /* invalid ion fd */
+    int ret = ion_sync_fd(0, m_validShareFd);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_sync_fd(-1, m_validShareFd));
+    /* bad share_fd */
+    EXPECT_EQ(-EINVAL, ion_sync_fd(m_ionFd, 0));
+    /* invalid share_fd */
+    EXPECT_EQ(-EBADF, ion_sync_fd(m_ionFd, -1));
+}
diff --git a/libion/tests/ion_test_fixture.cpp b/libion/tests/ion_test_fixture.cpp
new file mode 100644
index 0000000..e20c730
--- /dev/null
+++ b/libion/tests/ion_test_fixture.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 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 <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+IonTest::IonTest() : m_ionFd(-1)
+{
+}
+
+void IonTest::SetUp() {
+    m_ionFd = ion_open();
+    ASSERT_GE(m_ionFd, 0);
+}
+
+void IonTest::TearDown() {
+    ion_close(m_ionFd);
+}
+
+IonAllHeapsTest::IonAllHeapsTest() :
+        m_firstHeap(0),
+        m_lastHeap(0),
+        m_allHeaps()
+{
+}
+
+void IonAllHeapsTest::SetUp() {
+    int fd = ion_open();
+    ASSERT_GE(fd, 0);
+
+    for (int i = 1; i != 0; i <<= 1) {
+        ion_user_handle_t handle = 0;
+        int ret;
+        ret = ion_alloc(fd, 4096, 0, i, 0, &handle);
+        if (ret == 0 && handle != 0) {
+            ion_free(fd, handle);
+            if (!m_firstHeap) {
+                m_firstHeap = i;
+            }
+            m_lastHeap = i;
+            m_allHeaps.push_back(i);
+        } else {
+            ASSERT_EQ(-ENODEV, ret);
+        }
+    }
+    ion_close(fd);
+
+    EXPECT_NE(0U, m_firstHeap);
+    EXPECT_NE(0U, m_lastHeap);
+
+    RecordProperty("Heaps", m_allHeaps.size());
+    IonTest::SetUp();
+}
+
+void IonAllHeapsTest::TearDown() {
+    IonTest::TearDown();
+}
diff --git a/libion/tests/ion_test_fixture.h b/libion/tests/ion_test_fixture.h
new file mode 100644
index 0000000..4098214
--- /dev/null
+++ b/libion/tests/ion_test_fixture.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 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 ION_TEST_FIXTURE_H_
+#define ION_TEST_FIXTURE_H_
+
+#include <gtest/gtest.h>
+
+using ::testing::Test;
+
+class IonTest : public virtual Test {
+ public:
+    IonTest();
+	virtual ~IonTest() {};
+	virtual void SetUp();
+	virtual void TearDown();
+	int m_ionFd;
+};
+
+class IonAllHeapsTest : public IonTest {
+ public:
+    IonAllHeapsTest();
+    virtual ~IonAllHeapsTest() {};
+    virtual void SetUp();
+    virtual void TearDown();
+
+    unsigned int m_firstHeap;
+    unsigned int m_lastHeap;
+
+    std::vector<unsigned int> m_allHeaps;
+};
+
+#endif /* ION_TEST_FIXTURE_H_ */
diff --git a/libion/tests/map_test.cpp b/libion/tests/map_test.cpp
new file mode 100644
index 0000000..c006dc8
--- /dev/null
+++ b/libion/tests/map_test.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2013 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 <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+class Map : public IonAllHeapsTest {
+};
+
+TEST_F(Map, MapHandle)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            ion_user_handle_t handle = 0;
+
+            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+            ASSERT_TRUE(handle != 0);
+
+            int map_fd = -1;
+            unsigned char *ptr = NULL;
+            ASSERT_EQ(0, ion_map(m_ionFd, handle, size, PROT_READ | PROT_WRITE, MAP_SHARED, 0, &ptr, &map_fd));
+            ASSERT_TRUE(ptr != NULL);
+            ASSERT_GE(map_fd, 0);
+
+            ASSERT_EQ(0, close(map_fd));
+
+            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+
+            memset(ptr, 0xaa, size);
+
+            ASSERT_EQ(0, munmap(ptr, size));
+        }
+    }
+}
+
+TEST_F(Map, MapFd)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            int map_fd = -1;
+
+            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+            ASSERT_GE(map_fd, 0);
+
+            void *ptr;
+            ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+            ASSERT_TRUE(ptr != NULL);
+
+            ASSERT_EQ(0, close(map_fd));
+
+            memset(ptr, 0xaa, size);
+
+            ASSERT_EQ(0, munmap(ptr, size));
+        }
+    }
+}
+
+TEST_F(Map, MapOffset)
+{
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, PAGE_SIZE * 2, 0, heapMask, 0, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        unsigned char *ptr;
+        ptr = (unsigned char *)mmap(NULL, PAGE_SIZE * 2, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        memset(ptr, 0, PAGE_SIZE);
+        memset(ptr + PAGE_SIZE, 0xaa, PAGE_SIZE);
+
+        ASSERT_EQ(0, munmap(ptr, PAGE_SIZE * 2));
+
+        ptr = (unsigned char *)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, PAGE_SIZE);
+        ASSERT_TRUE(ptr != NULL);
+
+        ASSERT_EQ(ptr[0], 0xaa);
+        ASSERT_EQ(ptr[PAGE_SIZE - 1], 0xaa);
+
+        ASSERT_EQ(0, munmap(ptr, PAGE_SIZE));
+
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+
+TEST_F(Map, MapCached)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            int map_fd = -1;
+            unsigned int flags = ION_FLAG_CACHED;
+
+            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
+            ASSERT_GE(map_fd, 0);
+
+            void *ptr;
+            ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+            ASSERT_TRUE(ptr != NULL);
+
+            ASSERT_EQ(0, close(map_fd));
+
+            memset(ptr, 0xaa, size);
+
+            ASSERT_EQ(0, munmap(ptr, size));
+        }
+    }
+}
+
+TEST_F(Map, MapCachedNeedsSync)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            int map_fd = -1;
+            unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
+            ASSERT_GE(map_fd, 0);
+
+            void *ptr;
+            ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+            ASSERT_TRUE(ptr != NULL);
+
+            ASSERT_EQ(0, close(map_fd));
+
+            memset(ptr, 0xaa, size);
+
+            ASSERT_EQ(0, munmap(ptr, size));
+        }
+    }
+}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 50cbbfe..1129206 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -434,6 +434,7 @@
     disabled
     user shell
     group log
+    seclabel u:r:shell:s0
 
 on property:ro.debuggable=1
     start console