Add a simple smoke test for GL_EXT_external_buffer.

Updated the makefile to allow usage of <string>.

Change-Id: I1b3b4bbc10e97442b6ac8067021aa10d19d6d612
Test: ran android.vr.cts on a Pixel device
Bug: 62660866
diff --git a/tests/vr/jni/Android.mk b/tests/vr/jni/Android.mk
index 26b267c..769b9ef 100644
--- a/tests/vr/jni/Android.mk
+++ b/tests/vr/jni/Android.mk
@@ -30,4 +30,6 @@
 
 LOCAL_SDK_VERSION := current
 
+LOCAL_NDK_STL_VARIANT := c++_static
+
 include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/vr/jni/VrExtensionsJni.cpp b/tests/vr/jni/VrExtensionsJni.cpp
index 571d83f..7ae832d9 100644
--- a/tests/vr/jni/VrExtensionsJni.cpp
+++ b/tests/vr/jni/VrExtensionsJni.cpp
@@ -21,6 +21,11 @@
 #include <jni.h>
 #include <stdlib.h>
 #include <android/hardware_buffer.h>
+#include <android/log.h>
+#include <string>
+
+#define  LOG_TAG    "VrExtensionsJni"
+#define  LOGV(...)  __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
 
 using PFNEGLGETNATIVECLIENTBUFFERANDROID =
         EGLClientBuffer(EGLAPIENTRYP)(const AHardwareBuffer* buffer);
@@ -28,12 +33,34 @@
 using PFNGLEGLIMAGETARGETTEXTURE2DOESPROC = void(GL_APIENTRYP)(GLenum target,
                                                                void* image);
 
+using PFNGLBUFFERSTORAGEEXTERNALEXTPROC =
+    void(GL_APIENTRYP)(GLenum target, GLintptr offset, GLsizeiptr size,
+                       void* clientBuffer, GLbitfield flags);
+
+using PFNGLMAPBUFFERRANGEPROC = void*(GL_APIENTRYP)(GLenum target,
+                                                    GLintptr offset,
+                                                    GLsizeiptr length,
+                                                    GLbitfield access);
+
+using PFNGLUNMAPBUFFERPROC = void*(GL_APIENTRYP)(GLenum target);
+
 PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
 PFNEGLGETNATIVECLIENTBUFFERANDROID eglGetNativeClientBufferANDROID;
 PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
 PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
 PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC
     glFramebufferTextureMultisampleMultiviewOVR;
+PFNGLBUFFERSTORAGEEXTERNALEXTPROC glBufferStorageExternalEXT;
+PFNGLMAPBUFFERRANGEPROC glMapBufferRange;
+PFNGLUNMAPBUFFERPROC glUnmapBuffer;
+
+#define NO_ERROR 0
+#define GL_UNIFORM_BUFFER         0x8A11
+
+// Declare flags that are added to MapBufferRange via EXT_buffer_storage.
+// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_buffer_storage.txt
+#define GL_MAP_PERSISTENT_BIT_EXT 0x0040
+#define GL_MAP_COHERENT_BIT_EXT   0x0080
 
 #define LOAD_PROC(NAME, TYPE)                                           \
     NAME = reinterpret_cast<TYPE>(eglGetProcAddress(# NAME))
@@ -52,8 +79,8 @@
     ASSERT((a) == (b), "assert failed on (" #a ") at " __FILE__ ":%d", __LINE__)
 #define ASSERT_NE(a, b) \
     ASSERT((a) != (b), "assert failed on (" #a ") at " __FILE__ ":%d", __LINE__)
-#define ASSERT_LE(a, b) \
-    ASSERT((a) <= (b), "assert failed on (" #a ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_GT(a, b) \
+    ASSERT((a) > (b), "assert failed on (" #a ") at " __FILE__ ":%d", __LINE__)
 
 void fail(JNIEnv* env, const char* format, ...) {
     va_list args;
@@ -70,7 +97,7 @@
 
 static void testEglImageArray(JNIEnv* env, AHardwareBuffer_Desc desc,
                               int nsamples) {
-    ASSERT_LE(1, desc.layers);
+    ASSERT_GT(desc.layers, 1);
     AHardwareBuffer* hwbuffer = nullptr;
     int error = AHardwareBuffer_allocate(&desc, &hwbuffer);
     ASSERT_FALSE(error);
@@ -155,3 +182,85 @@
       }
     }
 }
+
+static void testExternalBuffer(JNIEnv* env, uint64_t usage, bool write_hwbuffer,
+                               const std::string& test_string) {
+    // Create a blob AHardwareBuffer suitable for holding the string.
+    AHardwareBuffer_Desc desc = {};
+    desc.width = test_string.size();
+    desc.height = 1;
+    desc.layers = 1;
+    desc.format = AHARDWAREBUFFER_FORMAT_BLOB;
+    desc.usage = usage;
+    AHardwareBuffer* hwbuffer = nullptr;
+    int error = AHardwareBuffer_allocate(&desc, &hwbuffer);
+    ASSERT_EQ(error, NO_ERROR);
+    // Create EGLClientBuffer from the AHardwareBuffer.
+    EGLClientBuffer native_buffer = eglGetNativeClientBufferANDROID(hwbuffer);
+    ASSERT_TRUE(native_buffer);
+    // Create uniform buffer from EGLClientBuffer.
+    const GLbitfield flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT |
+        GL_MAP_COHERENT_BIT_EXT | GL_MAP_PERSISTENT_BIT_EXT;
+    GLuint buf = 0;
+    glGenBuffers(1, &buf);
+    glBindBuffer(GL_UNIFORM_BUFFER, buf);
+    ASSERT_EQ(glGetError(), GL_NO_ERROR);
+    const GLsizeiptr bufsize = desc.width * desc.height;
+    glBufferStorageExternalEXT(GL_UNIFORM_BUFFER, 0,
+             bufsize, native_buffer, flags);
+    ASSERT_EQ(glGetError(), GL_NO_ERROR);
+    // Obtain a writeable pointer using either OpenGL or the Android API,
+    // then copy the test string into it.
+    if (write_hwbuffer) {
+      void* data = nullptr;
+      error = AHardwareBuffer_lock(hwbuffer,
+                                   AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, -1,
+                                   NULL, &data);
+      ASSERT_EQ(error, NO_ERROR);
+      ASSERT_TRUE(data);
+      memcpy(data, test_string.c_str(), test_string.size());
+      error = AHardwareBuffer_unlock(hwbuffer, nullptr);
+      ASSERT_EQ(error, NO_ERROR);
+    } else {
+      void* data =
+          glMapBufferRange(GL_UNIFORM_BUFFER, 0, bufsize,
+                           GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT_EXT);
+      ASSERT_EQ(glGetError(), GL_NO_ERROR);
+      ASSERT_TRUE(data);
+      memcpy(data, test_string.c_str(), test_string.size());
+      glUnmapBuffer(GL_UNIFORM_BUFFER);
+      ASSERT_EQ(glGetError(), GL_NO_ERROR);
+    }
+    // Obtain a readable pointer and verify the data.
+    void* data = glMapBufferRange(GL_UNIFORM_BUFFER, 0, bufsize, GL_MAP_READ_BIT);
+    ASSERT_TRUE(data);
+    ASSERT_EQ(strncmp(static_cast<char*>(data), test_string.c_str(),
+                      test_string.size()), 0);
+    glUnmapBuffer(GL_UNIFORM_BUFFER);
+    ASSERT_EQ(glGetError(), GL_NO_ERROR);
+    AHardwareBuffer_release(hwbuffer);
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_android_vr_cts_VrExtensionBehaviorTest_nativeTestExternalBuffer(
+    JNIEnv* env, jclass /* unused */) {
+    // First, check for EXT_external_buffer in the extension string.
+    auto exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
+    ASSERT_TRUE(exts && strstr(exts, "GL_EXT_external_buffer"));
+    // Next, load entry points provided by extensions.
+    LOAD_PROC(glBufferStorageExternalEXT, PFNGLBUFFERSTORAGEEXTERNALEXTPROC);
+    ASSERT_NE(glBufferStorageExternalEXT, nullptr);
+    LOAD_PROC(glMapBufferRange, PFNGLMAPBUFFERRANGEPROC);
+    ASSERT_NE(glMapBufferRange, nullptr);
+    LOAD_PROC(glUnmapBuffer, PFNGLUNMAPBUFFERPROC);
+    ASSERT_NE(glUnmapBuffer, nullptr);
+    const uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+        AHARDWAREBUFFER_USAGE_CPU_READ_RARELY |
+        AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER |
+        AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA;
+    const std::string test_string = "Hello, world.";
+    // First try writing to the buffer using OpenGL, then try writing to it via
+    // the AHardwareBuffer API.
+    testExternalBuffer(env, usage, false, test_string);
+    testExternalBuffer(env, usage, true, test_string);
+}
diff --git a/tests/vr/src/android/vr/cts/RendererProtectedTexturesTest.java b/tests/vr/src/android/vr/cts/RendererProtectedTexturesTest.java
index 8b0788b..2a24ec0 100644
--- a/tests/vr/src/android/vr/cts/RendererProtectedTexturesTest.java
+++ b/tests/vr/src/android/vr/cts/RendererProtectedTexturesTest.java
@@ -32,7 +32,7 @@
             + "}                         \n";
 
     private String fragmentShaderCode = "precision mediump float;  \n"
-            + "sampler2D protectedTexture;\n"
+            + "uniform sampler2D protectedTexture;\n"
             + "void main(){              \n"
             + " gl_FragColor = texture2D(protectedTexture, vec2(0.76953125, 0.22265625)); \n"
             + "}  \n";
@@ -72,4 +72,3 @@
         GLES20.glUniform1i(loc, 2);
     }
 }
-
diff --git a/tests/vr/src/android/vr/cts/VrExtensionBehaviorTest.java b/tests/vr/src/android/vr/cts/VrExtensionBehaviorTest.java
index f364241..a27633a 100644
--- a/tests/vr/src/android/vr/cts/VrExtensionBehaviorTest.java
+++ b/tests/vr/src/android/vr/cts/VrExtensionBehaviorTest.java
@@ -157,6 +157,8 @@
 
     /**
      * Test that a layered EGLImage can be created and attached to a FBO.
+     * For more information, see the EGL_image_array spec:
+     * https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_EGL_image_array.txt
      */
     public void testEglImageArray() throws Throwable {
         mActivity = getGlEsActivity(OpenGLESActivity.RENDERER_BASIC, 0, 0, 0);
@@ -173,6 +175,25 @@
     }
 
     /**
+     * Test that an external buffer can be created, written to, and read from.
+     * For more information, see the GL_EXT_external_buffer spec:
+     * https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_external_buffer.txt
+     */
+    public void testExternalBuffer() throws Throwable {
+        mActivity = getGlEsActivity(OpenGLESActivity.RENDERER_BASIC, 0, 0, 0);
+        if (!mActivity.supportsVrHighPerformance())
+            return;
+
+        assertEquals(GLES32.GL_NO_ERROR, mActivity.glGetError());
+
+        mActivity.runOnGlThread(new Runnable() {
+            public void run() {
+              nativeTestExternalBuffer();
+            }
+        });
+    }
+
+    /**
      * Runs a context priority test.
      */
     private void runContextPriorityTest(int priority) throws Throwable {
@@ -201,4 +222,5 @@
     }
 
     private static native boolean nativeTestEglImageArray();
+    private static native boolean nativeTestExternalBuffer();
 }