Implement an Android NDK build of SwiftShader using the CMake files.

From the build directory, run:

  cmake -DCMAKE_TOOLCHAIN_FILE=../build/android.toolchain.cmake ..

Everything compiles and links. unittests, vk-unittests and ReactorUnitTests are known to work.
A "rundroid.sh" script is provided to upload and run Android binaries.
This CL has contains the first draft of an NDK-based FrameBuffer implementation.
It stubs out the gralloc calls in Image (consequence: EGLImage likely won't work).

NOTE: a small CMake patch is necessary; hopefully we'll find a way around this or land it in
CMake:

diff cmake-3.10/Modules/Platform/Android/Determine-Compiler-NDK.cmake.bak cmake-3.10/Modules/Platform/Android/Determine-Compiler-NDK.cmake
231c231
<   set(_ANDROID_TOOL_C_COMPILER_EXTERNAL_TOOLCHAIN ${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_NAME}/prebuilt/${_ANDROID_HOST_DIR})
---
>   set(_ANDROID_TOOL_C_COMPILER_EXTERNAL_TOOLCHAIN ${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_LLVM_NAME}/prebuilt/${_ANDROID_HOST_DIR})

Bug: b/129942368
Change-Id: I107a2f719256b6477ad105054ca68c676c05ec5c
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/27869
Presubmit-Ready: Stephen White <senorblanco@chromium.org>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Tested-by: Stephen White <senorblanco@chromium.org>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 06c71ac..1ddb0ff 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,6 +18,9 @@
 
 if(CMAKE_SYSTEM_NAME MATCHES "Linux")
     set(LINUX ON)
+elseif(CMAKE_SYSTEM_NAME MATCHES "Android")
+    set(ANDROID ON)
+    set(CMAKE_CXX_FLAGS "-DANDROID_NDK_BUILD")
 elseif(WIN32)
 elseif(APPLE)
 else()
@@ -686,6 +689,8 @@
     set(LLVM_INCLUDE_DIR ${LLVM_DIR}/include-linux)
 elseif(APPLE)
     set(LLVM_INCLUDE_DIR ${LLVM_DIR}/include-osx)
+elseif(ANDROID)
+    set(LLVM_INCLUDE_DIR ${LLVM_DIR}/include-android)
 endif()
 
 list(APPEND LLVM_INCLUDE_DIR
@@ -1752,6 +1757,8 @@
     list(APPEND LLVM_INCLUDE_DIR ${LLVM_CONFIG_DIR}/linux/include)
 elseif(APPLE)
     list(APPEND LLVM_INCLUDE_DIR ${LLVM_CONFIG_DIR}/darwin/include)
+elseif(ANDROID)
+    list(APPEND LLVM_INCLUDE_DIR ${LLVM_CONFIG_DIR}/android/include)
 endif()
 
 list(APPEND LLVM_INCLUDE_DIR
@@ -2125,6 +2132,14 @@
     list(APPEND OPENGL_COMPILER_LIST
         ${OPENGL_COMPILER_DIR}/ossource_posix.cpp
     )
+elseif(ANDROID)
+    list(APPEND SWIFTSHADER_LIST
+        ${SOURCE_DIR}/Main/FrameBufferAndroid.cpp
+        ${SOURCE_DIR}/Main/FrameBufferAndroid.hpp
+    )
+    list(APPEND OPENGL_COMPILER_LIST
+        ${OPENGL_COMPILER_DIR}/ossource_posix.cpp
+    )
 endif()
 
 if(WIN32)
@@ -2190,6 +2205,9 @@
         COMPILE_DEFINITIONS "EGL_EGLEXT_PROTOTYPES; EGLAPI=; NO_SANITIZE_FUNCTION=;"
         PREFIX ""
     )
+    if (ANDROID)
+        set_target_properties(libEGL PROPERTIES SUFFIX "_swiftshader.so")
+    endif ()
     set_shared_library_export_map(libEGL ${SOURCE_DIR}/OpenGL/libEGL)
     target_link_libraries(libEGL ${OS_LIBS})
     add_custom_command(
@@ -2210,6 +2228,9 @@
         COMPILE_DEFINITIONS "GL_GLEXT_PROTOTYPES; GL_API=; GL_APICALL=; GLAPI=; NO_SANITIZE_FUNCTION=;"
         PREFIX ""
     )
+    if (ANDROID)
+        set_target_properties(libGLESv2 PROPERTIES SUFFIX "_swiftshader.so")
+    endif ()
     set_shared_library_export_map(libGLESv2 ${SOURCE_DIR}/OpenGL/libGLESv2)
     target_link_libraries(libGLESv2 SwiftShader ${Reactor} GLCompiler ${OS_LIBS})
     add_custom_command(
@@ -2230,6 +2251,9 @@
         COMPILE_DEFINITIONS "GL_GLEXT_PROTOTYPES; EGLAPI=; GL_API=; GL_APICALL=; GLAPI=;"
         PREFIX ""
     )
+    if (ANDROID)
+        set_target_properties(libGLES_CM PROPERTIES SUFFIX "_swiftshader.so")
+    endif ()
     set_shared_library_export_map(libGLES_CM ${SOURCE_DIR}/OpenGL/libGLES_CM)
     target_link_libraries(libGLES_CM SwiftShader ${Reactor} GLCompiler ${OS_LIBS})
     add_custom_command(
@@ -2358,6 +2382,9 @@
     )
 
     target_link_libraries(unittests libEGL libGLESv2 ${OS_LIBS})
+    if(ANDROID)
+        target_link_libraries(unittests -landroid)
+    endif()
 endif()
 
 if(BUILD_TESTS AND BUILD_VULKAN)
diff --git a/build/android.toolchain.cmake b/build/android.toolchain.cmake
new file mode 100644
index 0000000..d9806e3
--- /dev/null
+++ b/build/android.toolchain.cmake
@@ -0,0 +1,13 @@
+set(CMAKE_SYSTEM_NAME Android)
+if(NOT $ENV{ANDROID_HOME} STREQUAL "")
+  set(CMAKE_ANDROID_NDK $ENV{ANDROID_HOME}/ndk-bundle)
+else()
+  set(CMAKE_ANDROID_NDK $ENV{HOME}/Android/Sdk/ndk-bundle)
+endif()
+set(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION clang)
+set(CMAKE_ANDROID_ARCH_ABI arm64-v8a)
+set(CMAKE_ANDROID_STL_TYPE c++_shared)
+if(APPLE)
+  set(CMAKE_RANLIB "${CMAKE_ANDROID_NDK}/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/aarch64-linux-android/bin/ranlib")
+  set(CMAKE_AR "${CMAKE_ANDROID_NDK}/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/aarch64-linux-android/bin/ar")
+endif()
diff --git a/src/Common/Debug.hpp b/src/Common/Debug.hpp
index 9758c3b..0c862d4 100644
--- a/src/Common/Debug.hpp
+++ b/src/Common/Debug.hpp
@@ -15,7 +15,7 @@
 #ifndef Debug_hpp
 #define Debug_hpp
 
-#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD)
+#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD) && !defined(ANDROID_NDK_BUILD)
 #include "DebugAndroid.hpp"
 #else
 
diff --git a/src/Main/FrameBufferAndroid.cpp b/src/Main/FrameBufferAndroid.cpp
index 0ae5f09..38247bf 100644
--- a/src/Main/FrameBufferAndroid.cpp
+++ b/src/Main/FrameBufferAndroid.cpp
@@ -14,12 +14,16 @@
 
 #include "FrameBufferAndroid.hpp"
 
+#ifndef ANDROID_NDK_BUILD
 #include "Common/GrallocAndroid.hpp"
-
 #include <system/window.h>
+#else
+#include <android/native_window.h>
+#endif
 
 namespace sw
 {
+#if !defined(ANDROID_NDK_BUILD)
 	inline int dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer)
 	{
 		#if ANDROID_PLATFORM_SDK_VERSION > 16
@@ -46,18 +50,23 @@
 			return window->cancelBuffer(window, buffer);
 		#endif
 	}
+#endif // !defined(ANDROID_NDK_BUILD)
 
 	FrameBufferAndroid::FrameBufferAndroid(ANativeWindow* window, int width, int height)
 		: FrameBuffer(width, height, false, false),
 		  nativeWindow(window), buffer(nullptr)
 	{
+#ifndef ANDROID_NDK_BUILD
 		nativeWindow->common.incRef(&nativeWindow->common);
 		native_window_set_usage(nativeWindow, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+#endif
 	}
 
 	FrameBufferAndroid::~FrameBufferAndroid()
 	{
+#ifndef ANDROID_NDK_BUILD
 		nativeWindow->common.decRef(&nativeWindow->common);
+#endif
 	}
 
 	void FrameBufferAndroid::blit(sw::Surface *source, const Rect *sourceRect, const Rect *destRect)
@@ -72,17 +81,51 @@
 				unlock();
 			}
 
+#ifndef ANDROID_NDK_BUILD
 			queueBuffer(nativeWindow, buffer, -1);
+#endif
 		}
 	}
 
 	void *FrameBufferAndroid::lock()
 	{
+
+#if defined(ANDROID_NDK_BUILD)
+		ANativeWindow_Buffer surfaceBuffer;
+		if (ANativeWindow_lock(nativeWindow, &surfaceBuffer, nullptr) != 0) {
+			TRACE("%s failed to lock buffer %p", __FUNCTION__, buffer);
+			return nullptr;
+		}
+		framebuffer = surfaceBuffer.bits;
+
+		if((surfaceBuffer.width < width) || (surfaceBuffer.height < height))
+		{
+			TRACE("lock failed: buffer of %dx%d too small for window of %dx%d",
+			      surfaceBuffer.width, surfaceBuffer.height, width, height);
+			return nullptr;
+		}
+
+		switch(surfaceBuffer.format)
+		{
+		case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:     format = FORMAT_A8B8G8R8; break;
+		case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:     format = FORMAT_X8B8G8R8; break;
+		case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
+			// Frame buffers are expected to have 16-bit or 32-bit colors, not 24-bit.
+			TRACE("Unsupported frame buffer format R8G8B8"); ASSERT(false);
+			format = FORMAT_R8G8B8;   // Wrong component order.
+			break;
+		case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:       format = FORMAT_R5G6B5; break;
+		default:
+			TRACE("Unsupported frame buffer format %d", surfaceBuffer.format); ASSERT(false);
+			format = FORMAT_NULL;
+			break;
+		}
+		stride = surfaceBuffer.stride * Surface::bytes(format);
+#else // !defined(ANDROID_NDK_BUILD)
 		if(dequeueBuffer(nativeWindow, &buffer) != 0)
 		{
 			return nullptr;
 		}
-
 		if(GrallocModule::getInstance()->lock(buffer->handle,
 		                 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
 		                 0, 0, buffer->width, buffer->height, &framebuffer) != 0)
@@ -117,8 +160,9 @@
 			format = FORMAT_NULL;
 			break;
 		}
-
 		stride = buffer->stride * Surface::bytes(format);
+#endif // !defined(ANDROID_NDK_BUILD)
+
 		return framebuffer;
 	}
 
@@ -132,10 +176,14 @@
 
 		framebuffer = nullptr;
 
+#ifdef ANDROID_NDK_BUILD
+		ANativeWindow_unlockAndPost(nativeWindow);
+#else
 		if(GrallocModule::getInstance()->unlock(buffer->handle) != 0)
 		{
 			TRACE("%s: badness unlock failed", __FUNCTION__);
 		}
+#endif
 	}
 }
 
diff --git a/src/OpenGL/common/Image.hpp b/src/OpenGL/common/Image.hpp
index 1a1dfd7..856283f 100644
--- a/src/OpenGL/common/Image.hpp
+++ b/src/OpenGL/common/Image.hpp
@@ -21,12 +21,12 @@
 #include <GLES3/gl3.h>
 #include <GLES2/gl2ext.h>
 
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD)
 #include <system/window.h>
 #include "../../Common/GrallocAndroid.hpp"
 #endif
 
-#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD)
+#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD) && !defined(ANDROID_NDK_BUILD)
 #include "../../Common/DebugAndroid.hpp"
 #define LOGLOCK(fmt, ...) // TRACE(fmt " tid=%d", ##__VA_ARGS__, gettid())
 #else
@@ -235,7 +235,7 @@
 	void loadStencilData(GLsizei width, GLsizei height, GLsizei depth, int inputPitch, int inputHeight, GLenum format, GLenum type, const void *input, void *buffer);
 };
 
-#ifdef __ANDROID__
+#if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD)
 
 inline GLenum GLPixelFormatFromAndroid(int halFormat)
 {
@@ -362,7 +362,7 @@
 	}
 };
 
-#endif  // __ANDROID__
+#endif  // __ANDROID__ && !defined(ANDROID_NDK_BUILD)
 
 }
 
diff --git a/src/OpenGL/common/debug.cpp b/src/OpenGL/common/debug.cpp
index 87f7ee2..b145555 100644
--- a/src/OpenGL/common/debug.cpp
+++ b/src/OpenGL/common/debug.cpp
@@ -17,6 +17,7 @@
 #include "common/debug.h"
 
 #ifdef  __ANDROID__
+#if !defined(ANDROID_NDK_BUILD)
 #include <utils/String8.h>
 #if ANDROID_PLATFORM_SDK_VERSION < 27
 #include <cutils/log.h>
@@ -26,13 +27,14 @@
 #error "ANDROID_PLATFORM_SDK_VERSION is not defined"
 #endif
 #endif
+#endif
 
 #include <stdio.h>
 #include <stdarg.h>
 
 namespace es
 {
-#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD)
+#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD) && !defined(ANDROID_NDK_BUILD)
 	static void output(const char *format, va_list vararg)
 	{
 		ALOGI("%s", android::String8::formatV(format, vararg).string());
diff --git a/src/OpenGL/common/debug.h b/src/OpenGL/common/debug.h
index f7bd972..ce488a2 100644
--- a/src/OpenGL/common/debug.h
+++ b/src/OpenGL/common/debug.h
@@ -17,7 +17,7 @@
 #ifndef COMMON_DEBUG_H_
 #define COMMON_DEBUG_H_
 
-#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD)
+#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD) && !defined(ANDROID_NDK_BUILD)
 #include "../../Common/DebugAndroid.hpp"
 #else
 #include <stdio.h>
diff --git a/src/OpenGL/compiler/ConstantUnion.h b/src/OpenGL/compiler/ConstantUnion.h
index 2a1eeed..8a5b773 100644
--- a/src/OpenGL/compiler/ConstantUnion.h
+++ b/src/OpenGL/compiler/ConstantUnion.h
@@ -15,7 +15,7 @@
 #ifndef _CONSTANT_UNION_INCLUDED_
 #define _CONSTANT_UNION_INCLUDED_
 
-#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD)
+#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD) && !defined(ANDROID_NDK_BUILD)
 #include "../../Common/DebugAndroid.hpp"
 #else
 #include <assert.h>
diff --git a/src/OpenGL/compiler/SymbolTable.h b/src/OpenGL/compiler/SymbolTable.h
index 2aad529..7cf41be 100644
--- a/src/OpenGL/compiler/SymbolTable.h
+++ b/src/OpenGL/compiler/SymbolTable.h
@@ -38,7 +38,7 @@
 //   are tracked in the intermediate representation, not the symbol table.
 //
 
-#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD)
+#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD) && !defined(ANDROID_NDK_BUILD)
 #include "../../Common/DebugAndroid.hpp"
 #else
 #include <assert.h>
diff --git a/src/OpenGL/compiler/debug.h b/src/OpenGL/compiler/debug.h
index ffeecaa..1bf999c 100644
--- a/src/OpenGL/compiler/debug.h
+++ b/src/OpenGL/compiler/debug.h
@@ -17,7 +17,7 @@
 #ifndef COMPILER_DEBUG_H_
 #define COMPILER_DEBUG_H_
 
-#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD)
+#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD) && !defined(ANDROID_NDK_BUILD)
 #include "../../Common/DebugAndroid.hpp"
 
 #define Trace(...) ((void)0)
diff --git a/src/OpenGL/libEGL/Config.cpp b/src/OpenGL/libEGL/Config.cpp
index 4c35f6b..1af9229 100644
--- a/src/OpenGL/libEGL/Config.cpp
+++ b/src/OpenGL/libEGL/Config.cpp
@@ -21,7 +21,7 @@
 #include "common/debug.h"
 
 #include <EGL/eglext.h>
-#ifdef __ANDROID__
+#if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD)
 #include <system/graphics.h>
 #endif
 
@@ -65,7 +65,7 @@
 		mBlueSize = 8;
 		mAlphaSize = 8;
 		mBindToTextureRGBA = EGL_TRUE;
-		#ifdef __ANDROID__
+		#if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD)
 			mNativeVisualID = HAL_PIXEL_FORMAT_BGRA_8888;
 		#else
 			mNativeVisualID = 2;   // Arbitrary; prefer over ABGR
@@ -77,7 +77,7 @@
 		mBlueSize = 8;
 		mAlphaSize = 8;
 		mBindToTextureRGBA = EGL_TRUE;
-		#ifdef __ANDROID__
+		#if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD)
 			mNativeVisualID = HAL_PIXEL_FORMAT_RGBA_8888;
 		#endif
 		break;
@@ -86,7 +86,7 @@
 		mGreenSize = 6;
 		mBlueSize = 5;
 		mAlphaSize = 0;
-		#ifdef __ANDROID__
+		#if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD)
 			mNativeVisualID = HAL_PIXEL_FORMAT_RGB_565;
 		#endif
 		break;
@@ -96,7 +96,7 @@
 		mBlueSize = 8;
 		mAlphaSize = 0;
 		mBindToTextureRGB = EGL_TRUE;
-		#ifdef __ANDROID__
+		#if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD)
 			mNativeVisualID = 0x1FF;   // HAL_PIXEL_FORMAT_BGRX_8888
 		#else
 			mNativeVisualID = 1;   // Arbitrary; prefer over XBGR
@@ -108,7 +108,7 @@
 		mBlueSize = 8;
 		mAlphaSize = 0;
 		mBindToTextureRGB = EGL_TRUE;
-		#ifdef __ANDROID__
+		#if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD)
 			mNativeVisualID = HAL_PIXEL_FORMAT_RGBX_8888;
 		#endif
 		break;
diff --git a/src/OpenGL/libEGL/Display.cpp b/src/OpenGL/libEGL/Display.cpp
index ac525f0..99503ea 100644
--- a/src/OpenGL/libEGL/Display.cpp
+++ b/src/OpenGL/libEGL/Display.cpp
@@ -25,7 +25,7 @@
 #include "common/debug.h"
 #include "Common/RecursiveLock.hpp"
 
-#ifdef __ANDROID__
+#if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD)
 #include <system/window.h>
 #include <sys/ioctl.h>
 #include <linux/fb.h>
@@ -671,11 +671,13 @@
 			ERR("%s called with window==NULL %s:%d", __FUNCTION__, __FILE__, __LINE__);
 			return false;
 		}
+	#if !defined(ANDROID_NDK_BUILD)
 		if(static_cast<ANativeWindow*>(window)->common.magic != ANDROID_NATIVE_WINDOW_MAGIC)
 		{
 			ERR("%s called with window==%p bad magic %s:%d", __FUNCTION__, window, __FILE__, __LINE__);
 			return false;
 		}
+	#endif // !defined(ANDROID_NDK_BUILD)
 		return true;
 	#elif defined(USE_X11)
 		if(nativeDisplay)
@@ -782,6 +784,7 @@
 		default: UNREACHABLE(bpp);   // Unexpected display mode color depth
 		}
 	#elif defined(__ANDROID__)
+	#if !defined(ANDROID_NDK_BUILD)
 		static const char *const framebuffer[] =
 		{
 			"/dev/graphics/fb0",
@@ -841,6 +844,7 @@
 				}
 			}
 		}
+	#endif // !defined_ANDROID_NDK_BUILD)
 
 		// No framebuffer device found, or we're in user space
 		return sw::FORMAT_X8B8G8R8;
diff --git a/src/OpenGL/libEGL/Surface.cpp b/src/OpenGL/libEGL/Surface.cpp
index b8bba5f..dd912ed 100644
--- a/src/OpenGL/libEGL/Surface.cpp
+++ b/src/OpenGL/libEGL/Surface.cpp
@@ -33,6 +33,9 @@
 #elif defined(__APPLE__)
 #include "OSXUtils.hpp"
 #endif
+#if defined(__ANDROID__) && defined(ANDROID_NDK_BUILD)
+#include <android/native_window.h>
+#endif
 
 #include <algorithm>
 
@@ -339,8 +342,13 @@
 		int windowWidth = client.right - client.left;
 		int windowHeight = client.bottom - client.top;
 	#elif defined(__ANDROID__)
+	#ifdef ANDROID_NDK_BUILD
+		int windowWidth = ANativeWindow_getWidth(window);
+		int windowHeight = ANativeWindow_getHeight(window);
+	#else
 		int windowWidth;  window->query(window, NATIVE_WINDOW_WIDTH, &windowWidth);
 		int windowHeight; window->query(window, NATIVE_WINDOW_HEIGHT, &windowHeight);
+	#endif
 	#elif defined(USE_X11)
 		XWindowAttributes windowAttributes;
 		Status status = libX11->XGetWindowAttributes((::Display*)display->getNativeDisplay(), window, &windowAttributes);
diff --git a/src/OpenGL/libEGL/libEGL.cpp b/src/OpenGL/libEGL/libEGL.cpp
index 912fe0c..51f5309 100644
--- a/src/OpenGL/libEGL/libEGL.cpp
+++ b/src/OpenGL/libEGL/libEGL.cpp
@@ -23,7 +23,7 @@
 #include "common/debug.h"
 #include "Common/Version.h"
 
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD)
 #include <system/window.h>
 #elif defined(USE_X11)
 #include "Main/libX11.hpp"
@@ -1170,7 +1170,7 @@
 		}
 	}
 
-	#if defined(__ANDROID__)
+	#if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD)
 		if(target == EGL_NATIVE_BUFFER_ANDROID)
 		{
 			ANativeWindowBuffer *nativeBuffer = reinterpret_cast<ANativeWindowBuffer*>(buffer);
diff --git a/src/Reactor/Debug.hpp b/src/Reactor/Debug.hpp
index 720b38a..9df0b38 100644
--- a/src/Reactor/Debug.hpp
+++ b/src/Reactor/Debug.hpp
@@ -15,7 +15,7 @@
 #ifndef Debug_hpp
 #define Debug_hpp
 
-#ifdef __ANDROID__
+#if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD)
 #include "DebugAndroid.hpp"
 #else
 
diff --git a/src/System/Debug.hpp b/src/System/Debug.hpp
index 9758c3b..0c862d4 100644
--- a/src/System/Debug.hpp
+++ b/src/System/Debug.hpp
@@ -15,7 +15,7 @@
 #ifndef Debug_hpp
 #define Debug_hpp
 
-#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD)
+#if defined(__ANDROID__) && !defined(ANDROID_HOST_BUILD) && !defined(ANDROID_NDK_BUILD)
 #include "DebugAndroid.hpp"
 #else
 
diff --git a/tests/VulkanUnitTests/Driver.cpp b/tests/VulkanUnitTests/Driver.cpp
index f059178..a851883 100644
--- a/tests/VulkanUnitTests/Driver.cpp
+++ b/tests/VulkanUnitTests/Driver.cpp
@@ -20,6 +20,9 @@
 #elif defined(__APPLE__)
 #    include "dlfcn.h"
 #    define OS_MAC 1
+#elif defined(__ANDROID__)
+#    include "dlfcn.h"
+#    define OS_ANDROID 1
 #elif defined(__linux__)
 #    include "dlfcn.h"
 #    define OS_LINUX 1
@@ -55,6 +58,8 @@
     return load("./build/Darwin/libvk_swiftshader.dylib");
 #elif OS_LINUX
     return load("./build/Linux/libvk_swiftshader.so");
+#elif OS_ANDROID
+    return load("libvk_swiftshader.so");
 #else
 #    error Unimplemented platform
 #endif
@@ -73,7 +78,7 @@
 {
 #if OS_WINDOWS
     dll = LoadLibraryA(path);
-#elif(OS_MAC || OS_LINUX)
+#elif(OS_MAC || OS_LINUX || OS_ANDROID)
     dll = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
 #else
     return false;
@@ -157,7 +162,7 @@
 {
 #if OS_WINDOWS
     return GetProcAddress((HMODULE)dll, name);
-#elif(OS_MAC || OS_LINUX)
+#elif(OS_MAC || OS_LINUX || OS_ANDROID)
     return dlsym(dll, name);
 #else
     return nullptr;
diff --git a/tests/rundroid.sh b/tests/rundroid.sh
new file mode 100755
index 0000000..abc5d4a
--- /dev/null
+++ b/tests/rundroid.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# Run an Android NDK binary on the connected device.
+#
+# Example usage:
+# $ cd <builddir>
+# $ make vk-unittests
+# $ ../rundroid vk-unittests
+
+if [ "$#" -lt 1 ]; then
+    echo "Usage: $0 <executable>"
+    exit 1
+fi
+
+dst_dir=/data/local/tmp
+path="$1"
+name="$(basename "$path")"
+shift
+
+if [ -z "$ANDROID_HOME" ]; then
+    ANDROID_HOME=$HOME/Android/Sdk
+fi
+
+set -e
+set -x
+
+for lib in libGLESv2_swiftshader.so libEGL_swiftshader.so libvk_swiftshader.so; do
+    adb push --sync "$lib" "${dst_dir}/${lib}"
+done
+
+adb push --sync "$ANDROID_HOME/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_shared.so" "${dst_dir}/libc++_shared.so"
+
+adb push --sync "$path" "${dst_dir}/${name}"
+adb shell "cd \"$dst_dir\"; chmod +x \"$name\"; LD_LIBRARY_PATH=. ./$name $*"