EGL Multifile Blobcache: Fix applyLRU

This CL contains updates our cache eviction to be an actual
LRU instead of random.

* Entries are now tracked in a sorted map such that old entries
  are accessed first in applyLRU.
* Access and update times are tracked with nanosecond accuracy.

Additional tests:
* CacheEvictionIsLRU

Based on work by: Igor Nazarov <i.nazarov@samsung.com>

Test: libEGL_test, EGL_test, ANGLE trace tests, apps
Bug: b/351867582, b/380483358
Flag: com.android.graphics.egl.flags.multifile_blobcache_advanced_usage
Change-Id: I0b6f407b6d5a29f9487f7d7604d723e701c6f6d5
diff --git a/opengl/libs/EGL/MultifileBlobCache.h b/opengl/libs/EGL/MultifileBlobCache.h
index fe477bc..3bd393f 100644
--- a/opengl/libs/EGL/MultifileBlobCache.h
+++ b/opengl/libs/EGL/MultifileBlobCache.h
@@ -49,9 +49,9 @@
 };
 
 struct MultifileEntryStats {
+    uint32_t entryHash;
     EGLsizeiANDROID valueSize;
     size_t fileSize;
-    time_t accessTime;
 };
 
 struct MultifileStatus {
@@ -104,6 +104,26 @@
     size_t mBufferSize;
 };
 
+#if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+struct MultifileTimeLess {
+    bool operator()(const struct timespec& t1, const struct timespec& t2) const {
+        if (t1.tv_sec == t2.tv_sec) {
+            // If seconds are equal, check nanoseconds
+            return t1.tv_nsec < t2.tv_nsec;
+        } else {
+            // Otherwise, compare seconds
+            return t1.tv_sec < t2.tv_sec;
+        }
+    }
+};
+
+// The third parameter here causes all entries to be sorted by access time,
+// so oldest will be accessed first in applyLRU
+using MultifileEntryStatsMap =
+        std::multimap<struct timespec, MultifileEntryStats, MultifileTimeLess>;
+using MultifileEntryStatsMapIter = MultifileEntryStatsMap::iterator;
+#endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+
 class MultifileBlobCache {
 public:
     MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
@@ -119,6 +139,7 @@
 
     size_t getTotalSize() const { return mTotalCacheSize; }
     size_t getTotalEntries() const { return mTotalCacheEntries; }
+    size_t getTotalCacheSizeDivisor() const { return mTotalCacheSizeDivisor; }
 
     const std::string& getCurrentBuildId() const { return mBuildId; }
     void setCurrentBuildId(const std::string& buildId) { mBuildId = buildId; }
@@ -128,10 +149,11 @@
 
 private:
     void trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize,
-                    time_t accessTime);
+                    const timespec& accessTime);
     bool contains(uint32_t entryHash) const;
     bool removeEntry(uint32_t entryHash);
     MultifileEntryStats getEntryStats(uint32_t entryHash);
+    void updateEntryTime(uint32_t entryHash, const timespec& newTime);
 
     bool createStatus(const std::string& baseDir);
     bool checkStatus(const std::string& baseDir);
@@ -155,8 +177,14 @@
     std::string mBuildId;
     uint32_t mCacheVersion;
 
+#if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+    std::unordered_map<uint32_t, MultifileEntryStatsMapIter> mEntries;
+    MultifileEntryStatsMap mEntryStats;
+#else
     std::unordered_set<uint32_t> mEntries;
     std::unordered_map<uint32_t, MultifileEntryStats> mEntryStats;
+#endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+
     std::unordered_map<uint32_t, MultifileHotCache> mHotCache;
 
     size_t mMaxKeySize;
@@ -165,6 +193,7 @@
     size_t mMaxTotalEntries;
     size_t mTotalCacheSize;
     size_t mTotalCacheEntries;
+    size_t mTotalCacheSizeDivisor;
     size_t mHotCacheLimit;
     size_t mHotCacheEntryLimit;
     size_t mHotCacheSize;