• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  ** Copyright 2011, The Android Open Source Project
3  **
4  ** Licensed under the Apache License, Version 2.0 (the "License");
5  ** you may not use this file except in compliance with the License.
6  ** You may obtain a copy of the License at
7  **
8  **     http://www.apache.org/licenses/LICENSE-2.0
9  **
10  ** Unless required by applicable law or agreed to in writing, software
11  ** distributed under the License is distributed on an "AS IS" BASIS,
12  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  ** See the License for the specific language governing permissions and
14  ** limitations under the License.
15  */
16 
17 #include "../egl_impl.h"
18 
19 #include "egl_cache.h"
20 #include "egl_display.h"
21 #include "egldefs.h"
22 
23 #include <fcntl.h>
24 #include <sys/mman.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 
29 #ifndef MAX_EGL_CACHE_ENTRY_SIZE
30 #define MAX_EGL_CACHE_ENTRY_SIZE (16 * 1024);
31 #endif
32 
33 #ifndef MAX_EGL_CACHE_KEY_SIZE
34 #define MAX_EGL_CACHE_KEY_SIZE (1024);
35 #endif
36 
37 #ifndef MAX_EGL_CACHE_SIZE
38 #define MAX_EGL_CACHE_SIZE (64 * 1024);
39 #endif
40 
41 // Cache size limits.
42 static const size_t maxKeySize = MAX_EGL_CACHE_KEY_SIZE;
43 static const size_t maxValueSize = MAX_EGL_CACHE_ENTRY_SIZE;
44 static const size_t maxTotalSize = MAX_EGL_CACHE_SIZE;
45 
46 // Cache file header
47 static const char* cacheFileMagic = "EGL$";
48 static const size_t cacheFileHeaderSize = 8;
49 
50 // The time in seconds to wait before saving newly inserted cache entries.
51 static const unsigned int deferredSaveDelay = 4;
52 
53 // ----------------------------------------------------------------------------
54 namespace android {
55 // ----------------------------------------------------------------------------
56 
57 #define BC_EXT_STR "EGL_ANDROID_blob_cache"
58 
59 //
60 // Callback functions passed to EGL.
61 //
setBlob(const void * key,EGLsizeiANDROID keySize,const void * value,EGLsizeiANDROID valueSize)62 static void setBlob(const void* key, EGLsizeiANDROID keySize,
63         const void* value, EGLsizeiANDROID valueSize) {
64     egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
65 }
66 
getBlob(const void * key,EGLsizeiANDROID keySize,void * value,EGLsizeiANDROID valueSize)67 static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize,
68         void* value, EGLsizeiANDROID valueSize) {
69     return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
70 }
71 
72 //
73 // egl_cache_t definition
74 //
egl_cache_t()75 egl_cache_t::egl_cache_t() :
76         mInitialized(false),
77         mBlobCache(NULL) {
78 }
79 
~egl_cache_t()80 egl_cache_t::~egl_cache_t() {
81 }
82 
83 egl_cache_t egl_cache_t::sCache;
84 
get()85 egl_cache_t* egl_cache_t::get() {
86     return &sCache;
87 }
88 
initialize(egl_display_t * display)89 void egl_cache_t::initialize(egl_display_t *display) {
90     Mutex::Autolock lock(mMutex);
91 
92     egl_connection_t* const cnx = &gEGLImpl;
93     if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
94         const char* exts = display->disp.queryString.extensions;
95         size_t bcExtLen = strlen(BC_EXT_STR);
96         size_t extsLen = strlen(exts);
97         bool equal = !strcmp(BC_EXT_STR, exts);
98         bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
99         bool atEnd = (bcExtLen+1) < extsLen &&
100                 !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
101         bool inMiddle = strstr(exts, " " BC_EXT_STR " ");
102         if (equal || atStart || atEnd || inMiddle) {
103             PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
104             eglSetBlobCacheFuncsANDROID =
105                     reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
106                             cnx->egl.eglGetProcAddress(
107                                     "eglSetBlobCacheFuncsANDROID"));
108             if (eglSetBlobCacheFuncsANDROID == NULL) {
109                 ALOGE("EGL_ANDROID_blob_cache advertised, "
110                         "but unable to get eglSetBlobCacheFuncsANDROID");
111                 return;
112             }
113 
114             eglSetBlobCacheFuncsANDROID(display->disp.dpy,
115                     android::setBlob, android::getBlob);
116             EGLint err = cnx->egl.eglGetError();
117             if (err != EGL_SUCCESS) {
118                 ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
119                         "%#x", err);
120             }
121         }
122     }
123 
124     mInitialized = true;
125 }
126 
terminate()127 void egl_cache_t::terminate() {
128     Mutex::Autolock lock(mMutex);
129     if (mBlobCache != NULL) {
130         saveBlobCacheLocked();
131         mBlobCache = NULL;
132     }
133     mInitialized = false;
134 }
135 
setBlob(const void * key,EGLsizeiANDROID keySize,const void * value,EGLsizeiANDROID valueSize)136 void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
137         const void* value, EGLsizeiANDROID valueSize) {
138     Mutex::Autolock lock(mMutex);
139 
140     if (keySize < 0 || valueSize < 0) {
141         ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
142         return;
143     }
144 
145     if (mInitialized) {
146         sp<BlobCache> bc = getBlobCacheLocked();
147         bc->set(key, keySize, value, valueSize);
148 
149         if (!mSavePending) {
150             class DeferredSaveThread : public Thread {
151             public:
152                 DeferredSaveThread() : Thread(false) {}
153 
154                 virtual bool threadLoop() {
155                     sleep(deferredSaveDelay);
156                     egl_cache_t* c = egl_cache_t::get();
157                     Mutex::Autolock lock(c->mMutex);
158                     if (c->mInitialized) {
159                         c->saveBlobCacheLocked();
160                     }
161                     c->mSavePending = false;
162                     return false;
163                 }
164             };
165 
166             // The thread will hold a strong ref to itself until it has finished
167             // running, so there's no need to keep a ref around.
168             sp<Thread> deferredSaveThread(new DeferredSaveThread());
169             mSavePending = true;
170             deferredSaveThread->run();
171         }
172     }
173 }
174 
getBlob(const void * key,EGLsizeiANDROID keySize,void * value,EGLsizeiANDROID valueSize)175 EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
176         void* value, EGLsizeiANDROID valueSize) {
177     Mutex::Autolock lock(mMutex);
178 
179     if (keySize < 0 || valueSize < 0) {
180         ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
181         return 0;
182     }
183 
184     if (mInitialized) {
185         sp<BlobCache> bc = getBlobCacheLocked();
186         return bc->get(key, keySize, value, valueSize);
187     }
188     return 0;
189 }
190 
setCacheFilename(const char * filename)191 void egl_cache_t::setCacheFilename(const char* filename) {
192     Mutex::Autolock lock(mMutex);
193     mFilename = filename;
194 }
195 
getBlobCacheLocked()196 sp<BlobCache> egl_cache_t::getBlobCacheLocked() {
197     if (mBlobCache == NULL) {
198         mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize);
199         loadBlobCacheLocked();
200     }
201     return mBlobCache;
202 }
203 
crc32c(const uint8_t * buf,size_t len)204 static uint32_t crc32c(const uint8_t* buf, size_t len) {
205     const uint32_t polyBits = 0x82F63B78;
206     uint32_t r = 0;
207     for (size_t i = 0; i < len; i++) {
208         r ^= buf[i];
209         for (int j = 0; j < 8; j++) {
210             if (r & 1) {
211                 r = (r >> 1) ^ polyBits;
212             } else {
213                 r >>= 1;
214             }
215         }
216     }
217     return r;
218 }
219 
saveBlobCacheLocked()220 void egl_cache_t::saveBlobCacheLocked() {
221     if (mFilename.length() > 0) {
222         size_t cacheSize = mBlobCache->getFlattenedSize();
223         size_t headerSize = cacheFileHeaderSize;
224         const char* fname = mFilename.string();
225 
226         // Try to create the file with no permissions so we can write it
227         // without anyone trying to read it.
228         int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
229         if (fd == -1) {
230             if (errno == EEXIST) {
231                 // The file exists, delete it and try again.
232                 if (unlink(fname) == -1) {
233                     // No point in retrying if the unlink failed.
234                     ALOGE("error unlinking cache file %s: %s (%d)", fname,
235                             strerror(errno), errno);
236                     return;
237                 }
238                 // Retry now that we've unlinked the file.
239                 fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
240             }
241             if (fd == -1) {
242                 ALOGE("error creating cache file %s: %s (%d)", fname,
243                         strerror(errno), errno);
244                 return;
245             }
246         }
247 
248         size_t fileSize = headerSize + cacheSize;
249 
250         uint8_t* buf = new uint8_t [fileSize];
251         if (!buf) {
252             ALOGE("error allocating buffer for cache contents: %s (%d)",
253                     strerror(errno), errno);
254             close(fd);
255             unlink(fname);
256             return;
257         }
258 
259         status_t err = mBlobCache->flatten(buf + headerSize, cacheSize, NULL,
260                 0);
261         if (err != OK) {
262             ALOGE("error writing cache contents: %s (%d)", strerror(-err),
263                     -err);
264             delete [] buf;
265             close(fd);
266             unlink(fname);
267             return;
268         }
269 
270         // Write the file magic and CRC
271         memcpy(buf, cacheFileMagic, 4);
272         uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
273         *crc = crc32c(buf + headerSize, cacheSize);
274 
275         if (write(fd, buf, fileSize) == -1) {
276             ALOGE("error writing cache file: %s (%d)", strerror(errno),
277                     errno);
278             delete [] buf;
279             close(fd);
280             unlink(fname);
281             return;
282         }
283 
284         delete [] buf;
285         fchmod(fd, S_IRUSR);
286         close(fd);
287     }
288 }
289 
loadBlobCacheLocked()290 void egl_cache_t::loadBlobCacheLocked() {
291     if (mFilename.length() > 0) {
292         size_t headerSize = cacheFileHeaderSize;
293 
294         int fd = open(mFilename.string(), O_RDONLY, 0);
295         if (fd == -1) {
296             if (errno != ENOENT) {
297                 ALOGE("error opening cache file %s: %s (%d)", mFilename.string(),
298                         strerror(errno), errno);
299             }
300             return;
301         }
302 
303         struct stat statBuf;
304         if (fstat(fd, &statBuf) == -1) {
305             ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
306             close(fd);
307             return;
308         }
309 
310         // Sanity check the size before trying to mmap it.
311         size_t fileSize = statBuf.st_size;
312         if (fileSize > maxTotalSize * 2) {
313             ALOGE("cache file is too large: %#llx", statBuf.st_size);
314             close(fd);
315             return;
316         }
317 
318         uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
319                 PROT_READ, MAP_PRIVATE, fd, 0));
320         if (buf == MAP_FAILED) {
321             ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
322                     errno);
323             close(fd);
324             return;
325         }
326 
327         // Check the file magic and CRC
328         size_t cacheSize = fileSize - headerSize;
329         if (memcmp(buf, cacheFileMagic, 4) != 0) {
330             ALOGE("cache file has bad mojo");
331             close(fd);
332             return;
333         }
334         uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
335         if (crc32c(buf + headerSize, cacheSize) != *crc) {
336             ALOGE("cache file failed CRC check");
337             close(fd);
338             return;
339         }
340 
341         status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize, NULL,
342                 0);
343         if (err != OK) {
344             ALOGE("error reading cache contents: %s (%d)", strerror(-err),
345                     -err);
346             munmap(buf, fileSize);
347             close(fd);
348             return;
349         }
350 
351         munmap(buf, fileSize);
352         close(fd);
353     }
354 }
355 
356 // ----------------------------------------------------------------------------
357 }; // namespace android
358 // ----------------------------------------------------------------------------
359