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