Move some of the logic from egl_cache_t into FileBlobCache

HWUI is using BlobCache to implement SkSL shader cache very
similar to egl_cache_t. Create a new class FileBlobCache,
that is used by both egl_cache_t and HWUI. FileBlobCache adds
the logic to save and load the BlobCache from a memory mapped
file.

Test: Built and ran Android. Verified that EGL cache still works
Test: for gmail and calc apps.
Bug: 66740665
Change-Id: Ia4d194963d039a60244f3bc76108844418adc99d
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 32c2d7e..d43c164 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -122,6 +122,16 @@
     },
 }
 
+cc_library_static {
+    name: "libEGL_blobCache",
+    defaults: ["egl_libs_defaults"],
+    srcs: [
+        "EGL/BlobCache.cpp",
+        "EGL/FileBlobCache.cpp",
+    ],
+    export_include_dirs: ["EGL"],
+}
+
 cc_library_shared {
     name: "libEGL",
     defaults: ["egl_libs_defaults"],
@@ -133,7 +143,6 @@
         "EGL/egl.cpp",
         "EGL/eglApi.cpp",
         "EGL/Loader.cpp",
-        "EGL/BlobCache.cpp",
     ],
     shared_libs: [
         "libvndksupport",
@@ -143,7 +152,10 @@
         "libhidltransport",
         "libutils",
     ],
-    static_libs: ["libEGL_getProcAddress"],
+    static_libs: [
+        "libEGL_getProcAddress",
+        "libEGL_blobCache",
+    ],
     ldflags: ["-Wl,--exclude-libs=ALL"],
     export_include_dirs: ["EGL/include"],
 }
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index 0624b60..b3752f5 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -37,9 +37,9 @@
 static const uint32_t blobCacheDeviceVersion = 1;
 
 BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
+        mMaxTotalSize(maxTotalSize),
         mMaxKeySize(maxKeySize),
         mMaxValueSize(maxValueSize),
-        mMaxTotalSize(maxTotalSize),
         mTotalSize(0) {
     int64_t now = std::chrono::steady_clock::now().time_since_epoch().count();
 #ifdef _WIN32
diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h
index a0a270a..1f5d535 100644
--- a/opengl/libs/EGL/BlobCache.h
+++ b/opengl/libs/EGL/BlobCache.h
@@ -97,6 +97,14 @@
     //
     int unflatten(void const* buffer, size_t size);
 
+protected:
+    // mMaxTotalSize is the maximum size that all cache entries can occupy. This
+    // includes space for both keys and values. When a call to BlobCache::set
+    // would otherwise cause this limit to be exceeded, either the key/value
+    // pair passed to BlobCache::set will not be cached or other cache entries
+    // will be evicted from the cache to make room for the new entry.
+    const size_t mMaxTotalSize;
+
 private:
     // Copying is disallowed.
     BlobCache(const BlobCache&);
@@ -220,13 +228,6 @@
     // simply not add the key/value pair to the cache.
     const size_t mMaxValueSize;
 
-    // mMaxTotalSize is the maximum size that all cache entries can occupy. This
-    // includes space for both keys and values. When a call to BlobCache::set
-    // would otherwise cause this limit to be exceeded, either the key/value
-    // pair passed to BlobCache::set will not be cached or other cache entries
-    // will be evicted from the cache to make room for the new entry.
-    const size_t mMaxTotalSize;
-
     // mTotalSize is the total combined size of all keys and values currently in
     // the cache.
     size_t mTotalSize;
diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp
new file mode 100644
index 0000000..ff608a3
--- /dev/null
+++ b/opengl/libs/EGL/FileBlobCache.cpp
@@ -0,0 +1,185 @@
+/*
+ ** Copyright 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 "FileBlobCache.h"
+
+#include <inttypes.h>
+#include <log/log.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+
+// Cache file header
+static const char* cacheFileMagic = "EGL$";
+static const size_t cacheFileHeaderSize = 8;
+
+namespace android {
+
+static uint32_t crc32c(const uint8_t* buf, size_t len) {
+    const uint32_t polyBits = 0x82F63B78;
+    uint32_t r = 0;
+    for (size_t i = 0; i < len; i++) {
+        r ^= buf[i];
+        for (int j = 0; j < 8; j++) {
+            if (r & 1) {
+                r = (r >> 1) ^ polyBits;
+            } else {
+                r >>= 1;
+            }
+        }
+    }
+    return r;
+}
+
+FileBlobCache::FileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
+        const std::string& filename)
+        : BlobCache(maxKeySize, maxValueSize, maxTotalSize)
+        , mFilename(filename) {
+    if (mFilename.length() > 0) {
+        size_t headerSize = cacheFileHeaderSize;
+
+        int fd = open(mFilename.c_str(), O_RDONLY, 0);
+        if (fd == -1) {
+            if (errno != ENOENT) {
+                ALOGE("error opening cache file %s: %s (%d)", mFilename.c_str(),
+                        strerror(errno), errno);
+            }
+            return;
+        }
+
+        struct stat statBuf;
+        if (fstat(fd, &statBuf) == -1) {
+            ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
+            close(fd);
+            return;
+        }
+
+        // Sanity check the size before trying to mmap it.
+        size_t fileSize = statBuf.st_size;
+        if (fileSize > mMaxTotalSize * 2) {
+            ALOGE("cache file is too large: %#" PRIx64,
+                  static_cast<off64_t>(statBuf.st_size));
+            close(fd);
+            return;
+        }
+
+        uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
+                PROT_READ, MAP_PRIVATE, fd, 0));
+        if (buf == MAP_FAILED) {
+            ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
+                    errno);
+            close(fd);
+            return;
+        }
+
+        // Check the file magic and CRC
+        size_t cacheSize = fileSize - headerSize;
+        if (memcmp(buf, cacheFileMagic, 4) != 0) {
+            ALOGE("cache file has bad mojo");
+            close(fd);
+            return;
+        }
+        uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
+        if (crc32c(buf + headerSize, cacheSize) != *crc) {
+            ALOGE("cache file failed CRC check");
+            close(fd);
+            return;
+        }
+
+        int err = unflatten(buf + headerSize, cacheSize);
+        if (err < 0) {
+            ALOGE("error reading cache contents: %s (%d)", strerror(-err),
+                    -err);
+            munmap(buf, fileSize);
+            close(fd);
+            return;
+        }
+
+        munmap(buf, fileSize);
+        close(fd);
+    }
+}
+
+void FileBlobCache::writeToFile() {
+    if (mFilename.length() > 0) {
+        size_t cacheSize = getFlattenedSize();
+        size_t headerSize = cacheFileHeaderSize;
+        const char* fname = mFilename.c_str();
+
+        // Try to create the file with no permissions so we can write it
+        // without anyone trying to read it.
+        int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
+        if (fd == -1) {
+            if (errno == EEXIST) {
+                // The file exists, delete it and try again.
+                if (unlink(fname) == -1) {
+                    // No point in retrying if the unlink failed.
+                    ALOGE("error unlinking cache file %s: %s (%d)", fname,
+                            strerror(errno), errno);
+                    return;
+                }
+                // Retry now that we've unlinked the file.
+                fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
+            }
+            if (fd == -1) {
+                ALOGE("error creating cache file %s: %s (%d)", fname,
+                        strerror(errno), errno);
+                return;
+            }
+        }
+
+        size_t fileSize = headerSize + cacheSize;
+
+        uint8_t* buf = new uint8_t [fileSize];
+        if (!buf) {
+            ALOGE("error allocating buffer for cache contents: %s (%d)",
+                    strerror(errno), errno);
+            close(fd);
+            unlink(fname);
+            return;
+        }
+
+        int err = flatten(buf + headerSize, cacheSize);
+        if (err < 0) {
+            ALOGE("error writing cache contents: %s (%d)", strerror(-err),
+                    -err);
+            delete [] buf;
+            close(fd);
+            unlink(fname);
+            return;
+        }
+
+        // Write the file magic and CRC
+        memcpy(buf, cacheFileMagic, 4);
+        uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
+        *crc = crc32c(buf + headerSize, cacheSize);
+
+        if (write(fd, buf, fileSize) == -1) {
+            ALOGE("error writing cache file: %s (%d)", strerror(errno),
+                    errno);
+            delete [] buf;
+            close(fd);
+            unlink(fname);
+            return;
+        }
+
+        delete [] buf;
+        fchmod(fd, S_IRUSR);
+        close(fd);
+    }
+}
+
+}
\ No newline at end of file
diff --git a/opengl/libs/EGL/FileBlobCache.h b/opengl/libs/EGL/FileBlobCache.h
new file mode 100644
index 0000000..393703f
--- /dev/null
+++ b/opengl/libs/EGL/FileBlobCache.h
@@ -0,0 +1,43 @@
+/*
+ ** Copyright 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_FILE_BLOB_CACHE_H
+#define ANDROID_FILE_BLOB_CACHE_H
+
+#include "BlobCache.h"
+#include <string>
+
+namespace android {
+
+class FileBlobCache : public BlobCache {
+public:
+    // FileBlobCache attempts to load the saved cache contents from disk into
+    // BlobCache.
+    FileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
+            const std::string& filename);
+
+    // writeToFile attempts to save the current contents of BlobCache to
+    // disk.
+    void writeToFile();
+
+private:
+    // mFilename is the name of the file for storing cache contents.
+    std::string mFilename;
+};
+
+} // namespace android
+
+#endif // ANDROID_BLOB_CACHE_H
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index 579e422..ec548f3 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -20,12 +20,8 @@
 
 #include "egl_display.h"
 
-
 #include <private/EGL/cache.h>
 
-#include <inttypes.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
 #include <unistd.h>
 
 #include <thread>
@@ -37,10 +33,6 @@
 static const size_t maxValueSize = 64 * 1024;
 static const size_t maxTotalSize = 2 * 1024 * 1024;
 
-// Cache file header
-static const char* cacheFileMagic = "EGL$";
-static const size_t cacheFileHeaderSize = 8;
-
 // The time in seconds to wait before saving newly inserted cache entries.
 static const unsigned int deferredSaveDelay = 4;
 
@@ -124,7 +116,9 @@
 
 void egl_cache_t::terminate() {
     std::lock_guard<std::mutex> lock(mMutex);
-    saveBlobCacheLocked();
+    if (mBlobCache) {
+        mBlobCache->writeToFile();
+    }
     mBlobCache = NULL;
 }
 
@@ -146,8 +140,8 @@
             std::thread deferredSaveThread([this]() {
                 sleep(deferredSaveDelay);
                 std::lock_guard<std::mutex> lock(mMutex);
-                if (mInitialized) {
-                    saveBlobCacheLocked();
+                if (mInitialized && mBlobCache) {
+                    mBlobCache->writeToFile();
                 }
                 mSavePending = false;
             });
@@ -179,163 +173,11 @@
 
 BlobCache* egl_cache_t::getBlobCacheLocked() {
     if (mBlobCache == nullptr) {
-        mBlobCache.reset(new BlobCache(maxKeySize, maxValueSize, maxTotalSize));
-        loadBlobCacheLocked();
+        mBlobCache.reset(new FileBlobCache(maxKeySize, maxValueSize, maxTotalSize, mFilename));
     }
     return mBlobCache.get();
 }
 
-static uint32_t crc32c(const uint8_t* buf, size_t len) {
-    const uint32_t polyBits = 0x82F63B78;
-    uint32_t r = 0;
-    for (size_t i = 0; i < len; i++) {
-        r ^= buf[i];
-        for (int j = 0; j < 8; j++) {
-            if (r & 1) {
-                r = (r >> 1) ^ polyBits;
-            } else {
-                r >>= 1;
-            }
-        }
-    }
-    return r;
-}
-
-void egl_cache_t::saveBlobCacheLocked() {
-    if (mFilename.length() > 0 && mBlobCache != NULL) {
-        size_t cacheSize = mBlobCache->getFlattenedSize();
-        size_t headerSize = cacheFileHeaderSize;
-        const char* fname = mFilename.c_str();
-
-        // Try to create the file with no permissions so we can write it
-        // without anyone trying to read it.
-        int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
-        if (fd == -1) {
-            if (errno == EEXIST) {
-                // The file exists, delete it and try again.
-                if (unlink(fname) == -1) {
-                    // No point in retrying if the unlink failed.
-                    ALOGE("error unlinking cache file %s: %s (%d)", fname,
-                            strerror(errno), errno);
-                    return;
-                }
-                // Retry now that we've unlinked the file.
-                fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
-            }
-            if (fd == -1) {
-                ALOGE("error creating cache file %s: %s (%d)", fname,
-                        strerror(errno), errno);
-                return;
-            }
-        }
-
-        size_t fileSize = headerSize + cacheSize;
-
-        uint8_t* buf = new uint8_t [fileSize];
-        if (!buf) {
-            ALOGE("error allocating buffer for cache contents: %s (%d)",
-                    strerror(errno), errno);
-            close(fd);
-            unlink(fname);
-            return;
-        }
-
-        int err = mBlobCache->flatten(buf + headerSize, cacheSize);
-        if (err < 0) {
-            ALOGE("error writing cache contents: %s (%d)", strerror(-err),
-                    -err);
-            delete [] buf;
-            close(fd);
-            unlink(fname);
-            return;
-        }
-
-        // Write the file magic and CRC
-        memcpy(buf, cacheFileMagic, 4);
-        uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
-        *crc = crc32c(buf + headerSize, cacheSize);
-
-        if (write(fd, buf, fileSize) == -1) {
-            ALOGE("error writing cache file: %s (%d)", strerror(errno),
-                    errno);
-            delete [] buf;
-            close(fd);
-            unlink(fname);
-            return;
-        }
-
-        delete [] buf;
-        fchmod(fd, S_IRUSR);
-        close(fd);
-    }
-}
-
-void egl_cache_t::loadBlobCacheLocked() {
-    if (mFilename.length() > 0) {
-        size_t headerSize = cacheFileHeaderSize;
-
-        int fd = open(mFilename.c_str(), O_RDONLY, 0);
-        if (fd == -1) {
-            if (errno != ENOENT) {
-                ALOGE("error opening cache file %s: %s (%d)", mFilename.c_str(),
-                        strerror(errno), errno);
-            }
-            return;
-        }
-
-        struct stat statBuf;
-        if (fstat(fd, &statBuf) == -1) {
-            ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
-            close(fd);
-            return;
-        }
-
-        // Sanity check the size before trying to mmap it.
-        size_t fileSize = statBuf.st_size;
-        if (fileSize > maxTotalSize * 2) {
-            ALOGE("cache file is too large: %#" PRIx64,
-                  static_cast<off64_t>(statBuf.st_size));
-            close(fd);
-            return;
-        }
-
-        uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
-                PROT_READ, MAP_PRIVATE, fd, 0));
-        if (buf == MAP_FAILED) {
-            ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
-                    errno);
-            close(fd);
-            return;
-        }
-
-        // Check the file magic and CRC
-        size_t cacheSize = fileSize - headerSize;
-        if (memcmp(buf, cacheFileMagic, 4) != 0) {
-            ALOGE("cache file has bad mojo");
-            close(fd);
-            return;
-        }
-        uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
-        if (crc32c(buf + headerSize, cacheSize) != *crc) {
-            ALOGE("cache file failed CRC check");
-            close(fd);
-            return;
-        }
-
-        int err = mBlobCache->unflatten(buf + headerSize, cacheSize);
-        if (err < 0) {
-            ALOGE("error reading cache contents: %s (%d)", strerror(-err),
-                    -err);
-            munmap(buf, fileSize);
-            close(fd);
-            return;
-        }
-
-        munmap(buf, fileSize);
-        close(fd);
-    }
-}
-
 // ----------------------------------------------------------------------------
 }; // namespace android
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index 56360f0..7382b91 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -20,7 +20,7 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include "BlobCache.h"
+#include "FileBlobCache.h"
 
 #include <memory>
 #include <mutex>
@@ -82,14 +82,6 @@
     // possible.
     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
@@ -101,7 +93,7 @@
     // 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.
-    std::unique_ptr<BlobCache> mBlobCache;
+    std::unique_ptr<FileBlobCache> mBlobCache;
 
     // mFilename is the name of the file for storing cache contents in between
     // program invocations.  It is initialized to an empty string at