EGL: use an in-memory the blob cache

This change makes the makes the stub EGL_ANDROID_blob_cache callbacks
actually use a BlobCache object.

Bug: 5474671
Change-Id: I5cbaae2dea3aad2fe306c9f57029c3f215a0863a
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index a08932a..8e8e26c 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -256,6 +256,21 @@
 typedef EGLuint64NV (EGLAPIENTRYP PFNEGLGETSYSTEMTIMENVPROC)(void);
 #endif
 
+
+/* EGL_ANDROID_blob_cache
+ */
+#ifndef EGL_ANDROID_blob_cache
+#define EGL_ANDROID_blob_cache 1
+typedef khronos_ssize_t EGLsizei;
+typedef void (*EGLSetBlobFunc) (const void* key, EGLsizei keySize, const void* value, EGLsizei valueSize);
+typedef EGLsizei (*EGLGetBlobFunc) (const void* key, EGLsizei keySize, void* value, EGLsizei valueSize);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI void EGLAPIENTRY eglSetBlobCacheFuncs(EGLDisplay dpy, EGLSetBlobFunc set, EGLGetBlobFunc get);
+#endif /* EGL_EGLEXT_PROTOTYPES */
+typedef void (EGLAPIENTRYP PFNEGLSETBLOBCACHEFUNCSPROC) (EGLDisplay dpy,
+        EGLSetBlobFunc set, EGLGetBlobFunc get);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index 1e64302..adb2a13 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -19,6 +19,11 @@
 #include "egl_impl.h"
 #include "egldefs.h"
 
+// Cache size limits.
+static const size_t maxKeySize = 1024;
+static const size_t maxValueSize = 4096;
+static const size_t maxTotalSize = 64 * 1024;
+
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
@@ -26,29 +31,27 @@
 #define BC_EXT_STR "EGL_ANDROID_blob_cache"
 
 //
-// EGL_ANDROID_blob_cache types and functions
-//
-typedef khronos_ssize_t EGLsizei;
-
-typedef void (*EGLSetBlobFunc) (const void* key, EGLsizei keySize,
-        const void* value, EGLsizei valueSize);
-
-typedef EGLsizei (*EGLGetBlobFunc) (const void* key, EGLsizei keySize,
-        void* value, EGLsizei valueSize);
-
-typedef void (EGLAPIENTRYP PFNEGLSETBLOBCACHEFUNCSPROC) (EGLDisplay dpy,
-        EGLSetBlobFunc set, EGLGetBlobFunc get);
-
-//
-// egl_cache_t definition
+// Callback functions passed to EGL.
 //
 static void setBlob(const void* key, EGLsizei keySize, const void* value,
         EGLsizei valueSize) {
+    egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
 }
 
 static EGLsizei getBlob(const void* key, EGLsizei keySize, void* value,
         EGLsizei valueSize) {
-    return 0;
+    return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
+}
+
+//
+// egl_cache_t definition
+//
+egl_cache_t::egl_cache_t() :
+        mInitialized(false),
+        mBlobCache(NULL) {
+}
+
+egl_cache_t::~egl_cache_t() {
 }
 
 egl_cache_t* egl_cache_t::get() {
@@ -57,6 +60,7 @@
 }
 
 void egl_cache_t::initialize(egl_display_t *display) {
+    Mutex::Autolock lock(mMutex);
     for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
         egl_connection_t* const cnx = &gEGLImpl[i];
         if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
@@ -79,7 +83,8 @@
                     continue;
                 }
 
-                eglSetBlobCacheFuncs(display->disp[i].dpy, setBlob, getBlob);
+                eglSetBlobCacheFuncs(display->disp[i].dpy, android::setBlob,
+                        android::getBlob);
                 EGLint err = cnx->egl.eglGetError();
                 if (err != EGL_SUCCESS) {
                     LOGE("eglSetBlobCacheFuncs resulted in an error: %#x",
@@ -88,6 +93,61 @@
             }
         }
     }
+    mInitialized = true;
+}
+
+void egl_cache_t::terminate() {
+    Mutex::Autolock lock(mMutex);
+    if (mBlobCache != NULL) {
+        saveBlobCacheLocked();
+        mBlobCache = NULL;
+    }
+    mInitialized = false;
+}
+
+void egl_cache_t::setBlob(const void* key, EGLsizei keySize, const void* value,
+        EGLsizei valueSize) {
+    Mutex::Autolock lock(mMutex);
+
+    if (keySize < 0 || valueSize < 0) {
+        LOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
+        return;
+    }
+
+    if (mInitialized) {
+        sp<BlobCache> bc = getBlobCacheLocked();
+        bc->set(key, keySize, value, valueSize);
+    }
+}
+
+EGLsizei egl_cache_t::getBlob(const void* key, EGLsizei keySize, void* value,
+        EGLsizei valueSize) {
+    Mutex::Autolock lock(mMutex);
+
+    if (keySize < 0 || valueSize < 0) {
+        LOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
+        return 0;
+    }
+
+    if (mInitialized) {
+        sp<BlobCache> bc = getBlobCacheLocked();
+        return bc->get(key, keySize, value, valueSize);
+    }
+    return 0;
+}
+
+sp<BlobCache> egl_cache_t::getBlobCacheLocked() {
+    if (mBlobCache == NULL) {
+        mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize);
+        loadBlobCacheLocked();
+    }
+    return mBlobCache;
+}
+
+void egl_cache_t::saveBlobCacheLocked() {
+}
+
+void egl_cache_t::loadBlobCacheLocked() {
 }
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index 1fcfacc..7021752 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -14,6 +14,15 @@
  ** limitations under the License.
  */
 
+#ifndef ANDROID_EGL_CACHE_H
+#define ANDROID_EGL_CACHE_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <utils/BlobCache.h>
+#include <utils/StrongPointer.h>
+
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
@@ -23,11 +32,77 @@
 class egl_cache_t {
 public:
 
+    // get returns a pointer to the singleton egl_cache_t object.  This
+    // singleton object will never be destroyed.
     static egl_cache_t* get();
 
+    // initialize puts the egl_cache_t into an initialized state, such that it
+    // is able to insert and retrieve entries from the cache.  This should be
+    // called when EGL is initialized.  When not in the initialized state the
+    // getBlob and setBlob methods will return without performing any cache
+    // operations.
     void initialize(egl_display_t* display);
+
+    // terminate puts the egl_cache_t back into the uninitialized state.  When
+    // in this state the getBlob and setBlob methods will return without
+    // performing any cache operations.
+    void terminate();
+
+    // setBlob attempts to insert a new key/value blob pair into the cache.
+    // This will be called by the hardware vendor's EGL implementation via the
+    // EGL_ANDROID_blob_cache extension.
+    void setBlob(const void* key, EGLsizei keySize, const void* value,
+        EGLsizei valueSize);
+
+    // getBlob attempts to retrieve the value blob associated with a given key
+    // blob from cache.  This will be called by the hardware vendor's EGL
+    // implementation via the EGL_ANDROID_blob_cache extension.
+    EGLsizei getBlob(const void* key, EGLsizei keySize, void* value,
+        EGLsizei valueSize);
+
+private:
+    // Creation and (the lack of) destruction is handled internally.
+    egl_cache_t();
+    ~egl_cache_t();
+
+    // Copying is disallowed.
+    egl_cache_t(const egl_cache_t&); // not implemented
+    void operator=(const egl_cache_t&); // not implemented
+
+    // getBlobCacheLocked returns the BlobCache object being used to store the
+    // key/value blob pairs.  If the BlobCache object has not yet been created,
+    // this will do so, loading the serialized cache contents from disk if
+    // possible.
+    sp<BlobCache> getBlobCacheLocked();
+
+    // saveBlobCache attempts to save the current contents of mBlobCache to
+    // disk.
+    void saveBlobCacheLocked();
+
+    // loadBlobCache attempts to load the saved cache contents from disk into
+    // mBlobCache.
+    void loadBlobCacheLocked();
+
+    // mInitialized indicates whether the egl_cache_t is in the initialized
+    // state.  It is initialized to false at construction time, and gets set to
+    // true when initialize is called.  It is set back to false when terminate
+    // is called.  When in this state, the cache behaves as normal.  When not,
+    // the getBlob and setBlob methods will return without performing any cache
+    // operations.
+    bool mInitialized;
+
+    // mBlobCache is the cache in which the key/value blob pairs are stored.  It
+    // is initially NULL, and will be initialized by getBlobCacheLocked the
+    // first time it's needed.
+    sp<BlobCache> mBlobCache;
+
+    // mMutex is the mutex used to prevent concurrent access to the member
+    // variables. It must be locked whenever the member variables are accessed.
+    mutable Mutex mMutex;
 };
 
 // ----------------------------------------------------------------------------
 }; // namespace android
 // ----------------------------------------------------------------------------
+
+#endif // ANDROID_EGL_CACHE_H
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 0f92864..558ca77 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -44,6 +44,7 @@
 
 egl_display_t::~egl_display_t() {
     magic = 0;
+    egl_cache_t::get()->terminate();
 }
 
 egl_display_t* egl_display_t::get(EGLDisplay dpy) {