Merge "Remove surface legacy APIs and code." into ics-mr1
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 3fa2acb..33b2f00 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -110,7 +110,8 @@
     
     // Place a file descriptor into the parcel.  The given fd must remain
     // valid for the lifetime of the parcel.
-    status_t            writeFileDescriptor(int fd);
+    // The Parcel does not take ownership of the given fd unless you ask it to.
+    status_t            writeFileDescriptor(int fd, bool takeOwnership = false);
     
     // Place a file descriptor into the parcel.  A dup of the fd is made, which
     // will be closed once the parcel is destroyed.
diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h
index dc45ff0..4f342a2 100644
--- a/include/utils/BlobCache.h
+++ b/include/utils/BlobCache.h
@@ -19,19 +19,21 @@
 
 #include <stddef.h>
 
+#include <utils/Flattenable.h>
 #include <utils/RefBase.h>
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 
 namespace android {
 
-// A BlobCache is an in-memory cache for binary key/value pairs. All the public
-// methods are thread-safe.
+// A BlobCache is an in-memory cache for binary key/value pairs.  A BlobCache
+// does NOT provide any thread-safety guarantees.
 //
-// The cache contents can be serialized to a file and reloaded in a subsequent
-// execution of the program. This serialization is non-portable and should only
-// be loaded by the device that generated it.
-class BlobCache : public RefBase {
+// The cache contents can be serialized to an in-memory buffer or mmap'd file
+// and then reloaded in a subsequent execution of the program.  This
+// serialization is non-portable and the data should only be used by the device
+// that generated it.
+class BlobCache : public RefBase, public Flattenable {
 public:
 
     // Create an empty blob cache. The blob cache will cache key/value pairs
@@ -58,14 +60,13 @@
     void set(const void* key, size_t keySize, const void* value,
             size_t valueSize);
 
-    // The get function retrieves from the cache the binary value associated
-    // with a given binary key.  If the key is present in the cache then the
-    // length of the binary value associated with that key is returned.  If the
-    // value argument is non-NULL and the size of the cached value is less than
-    // valueSize bytes then the cached value is copied into the buffer pointed
-    // to by the value argument.  If the key is not present in the cache then 0
-    // is returned and the buffer pointed to by the value argument is not
-    // modified.
+    // get retrieves from the cache the binary value associated with a given
+    // binary key.  If the key is present in the cache then the length of the
+    // binary value associated with that key is returned.  If the value argument
+    // is non-NULL and the size of the cached value is less than valueSize bytes
+    // then the cached value is copied into the buffer pointed to by the value
+    // argument.  If the key is not present in the cache then 0 is returned and
+    // the buffer pointed to by the value argument is not modified.
     //
     // Note that when calling get multiple times with the same key, the later
     // calls may fail, returning 0, even if earlier calls succeeded.  The return
@@ -77,6 +78,37 @@
     //   0 <= valueSize
     size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
 
+    // getFlattenedSize returns the number of bytes needed to store the entire
+    // serialized cache.
+    virtual size_t getFlattenedSize() const;
+
+    // getFdCount returns the number of file descriptors that will result from
+    // flattening the cache.  This will always return 0 so as to allow the
+    // flattened cache to be saved to disk and then later restored.
+    virtual size_t getFdCount() const;
+
+    // flatten serializes the current contents of the cache into the memory
+    // pointed to by 'buffer'.  The serialized cache contents can later be
+    // loaded into a BlobCache object using the unflatten method.  The contents
+    // of the BlobCache object will not be modified.
+    //
+    // Preconditions:
+    //   size >= this.getFlattenedSize()
+    //   count == 0
+    virtual status_t flatten(void* buffer, size_t size, int fds[],
+            size_t count) const;
+
+    // unflatten replaces the contents of the cache with the serialized cache
+    // contents in the memory pointed to by 'buffer'.  The previous contents of
+    // the BlobCache will be evicted from the cache.  If an error occurs while
+    // unflattening the serialized cache contents then the BlobCache will be
+    // left in an empty state.
+    //
+    // Preconditions:
+    //   count == 0
+    virtual status_t unflatten(void const* buffer, size_t size, int fds[],
+            size_t count);
+
 private:
     // Copying is disallowed.
     BlobCache(const BlobCache&);
@@ -144,6 +176,46 @@
         sp<Blob> mValue;
     };
 
+    // A Header is the header for the entire BlobCache serialization format. No
+    // need to make this portable, so we simply write the struct out.
+    struct Header {
+        // mMagicNumber is the magic number that identifies the data as
+        // serialized BlobCache contents.  It must always contain 'Blb$'.
+        uint32_t mMagicNumber;
+
+        // mBlobCacheVersion is the serialization format version.
+        uint32_t mBlobCacheVersion;
+
+        // mDeviceVersion is the device-specific version of the cache.  This can
+        // be used to invalidate the cache.
+        uint32_t mDeviceVersion;
+
+        // mNumEntries is number of cache entries following the header in the
+        // data.
+        size_t mNumEntries;
+    };
+
+    // An EntryHeader is the header for a serialized cache entry.  No need to
+    // make this portable, so we simply write the struct out.  Each EntryHeader
+    // is followed imediately by the key data and then the value data.
+    //
+    // The beginning of each serialized EntryHeader is 4-byte aligned, so the
+    // number of bytes that a serialized cache entry will occupy is:
+    //
+    //   ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
+    //
+    struct EntryHeader {
+        // mKeySize is the size of the entry key in bytes.
+        size_t mKeySize;
+
+        // mValueSize is the size of the entry value in bytes.
+        size_t mValueSize;
+
+        // mData contains both the key and value data for the cache entry.  The
+        // key comes first followed immediately by the value.
+        uint8_t mData[];
+    };
+
     // mMaxKeySize is the maximum key size that will be cached. Calls to
     // BlobCache::set with a keySize parameter larger than mMaxKeySize will
     // simply not add the key/value pair to the cache.
@@ -166,17 +238,12 @@
     size_t mTotalSize;
 
     // mRandState is the pseudo-random number generator state. It is passed to
-    // nrand48 to generate random numbers when needed. It must be protected by
-    // mMutex.
+    // nrand48 to generate random numbers when needed.
     unsigned short mRandState[3];
 
     // mCacheEntries stores all the cache entries that are resident in memory.
     // Cache entries are added to it by the 'set' method.
     SortedVector<CacheEntry> mCacheEntries;
-
-    // mMutex is used to synchronize access to all member variables.  It must be
-    // locked any time the member variables are written or read.
-    Mutex mMutex;
 };
 
 }
diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp
index bf8d7a6..0733378 100644
--- a/libs/binder/CursorWindow.cpp
+++ b/libs/binder/CursorWindow.cpp
@@ -209,7 +209,7 @@
     uint32_t offset = mHeader->freeOffset + padding;
     uint32_t nextFreeOffset = offset + size;
     if (nextFreeOffset > mSize) {
-        LOGE("Window is full: requested allocation %d bytes, "
+        LOGW("Window is full: requested allocation %d bytes, "
                 "free space %d bytes, window size %d bytes",
                 size, freeSpace(), mSize);
         return 0;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index c7180ce..6b4c1a6 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -710,24 +710,19 @@
     return err;
 }
 
-status_t Parcel::writeFileDescriptor(int fd)
+status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership)
 {
     flat_binder_object obj;
     obj.type = BINDER_TYPE_FD;
     obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
     obj.handle = fd;
-    obj.cookie = (void*)0;
+    obj.cookie = (void*) (takeOwnership ? 1 : 0);
     return writeObject(obj, true);
 }
 
 status_t Parcel::writeDupFileDescriptor(int fd)
 {
-    flat_binder_object obj;
-    obj.type = BINDER_TYPE_FD;
-    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
-    obj.handle = dup(fd);
-    obj.cookie = (void*)1;
-    return writeObject(obj, true);
+    return writeFileDescriptor(dup(fd), true /*takeOwnership*/);
 }
 
 status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob)
@@ -764,7 +759,7 @@
             } else {
                 status = writeInt32(1);
                 if (!status) {
-                    status = writeFileDescriptor(fd);
+                    status = writeFileDescriptor(fd, true /*takeOwnership*/);
                     if (!status) {
                         outBlob->init(true /*mapped*/, ptr, len);
                         return NO_ERROR;
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index c72a45b..6f84206 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -116,7 +116,7 @@
     // Choose a name using the PID and a process-unique ID.
     mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
 
-    ST_LOGV("SurfaceTexture::SurfaceTexture");
+    ST_LOGV("SurfaceTexture");
     sp<ISurfaceComposer> composer(ComposerService::getComposerService());
     mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
     mNextCrop.makeInvalid();
@@ -125,7 +125,7 @@
 }
 
 SurfaceTexture::~SurfaceTexture() {
-    ST_LOGV("SurfaceTexture::~SurfaceTexture");
+    ST_LOGV("~SurfaceTexture");
     freeAllBuffersLocked();
 }
 
@@ -169,7 +169,7 @@
 }
 
 status_t SurfaceTexture::setBufferCount(int bufferCount) {
-    ST_LOGV("SurfaceTexture::setBufferCount");
+    ST_LOGV("setBufferCount: count=%d", bufferCount);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -217,6 +217,7 @@
 
 status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
 {
+    ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h);
     if (!w || !h) {
         ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)",
                 w, h);
@@ -230,7 +231,7 @@
 }
 
 status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
-    ST_LOGV("SurfaceTexture::requestBuffer");
+    ST_LOGV("requestBuffer: slot=%d", slot);
     Mutex::Autolock lock(mMutex);
     if (mAbandoned) {
         ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!");
@@ -248,7 +249,7 @@
 
 status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
         uint32_t format, uint32_t usage) {
-    ST_LOGV("SurfaceTexture::dequeueBuffer");
+    ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage);
 
     if ((w && !h) || (!w && h)) {
         ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
@@ -342,6 +343,8 @@
         // clients are not allowed to dequeue more than one buffer
         // if they didn't set a buffer count.
         if (!mClientBufferCount && dequeuedCount) {
+            ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without "
+                    "setting the buffer count");
             return -EINVAL;
         }
 
@@ -375,6 +378,8 @@
     }
 
     if (found == INVALID_BUFFER_SLOT) {
+        // This should not happen.
+        ST_LOGE("dequeueBuffer: no available buffer slots");
         return -EBUSY;
     }
 
@@ -427,10 +432,13 @@
         }
         returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
     }
+    ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf,
+            mSlots[buf].mGraphicBuffer->handle, returnFlags);
     return returnFlags;
 }
 
 status_t SurfaceTexture::setSynchronousMode(bool enabled) {
+    ST_LOGV("setSynchronousMode: enabled=%d", enabled);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -462,7 +470,7 @@
 
 status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
         uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
-    ST_LOGV("SurfaceTexture::queueBuffer");
+    ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);
 
     sp<FrameAvailableListener> listener;
 
@@ -534,7 +542,7 @@
 }
 
 void SurfaceTexture::cancelBuffer(int buf) {
-    ST_LOGV("SurfaceTexture::cancelBuffer");
+    ST_LOGV("cancelBuffer: slot=%d", buf);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -556,7 +564,9 @@
 }
 
 status_t SurfaceTexture::setCrop(const Rect& crop) {
-    ST_LOGV("SurfaceTexture::setCrop");
+    ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right,
+            crop.bottom);
+
     Mutex::Autolock lock(mMutex);
     if (mAbandoned) {
         ST_LOGE("setCrop: SurfaceTexture has been abandoned!");
@@ -567,7 +577,7 @@
 }
 
 status_t SurfaceTexture::setTransform(uint32_t transform) {
-    ST_LOGV("SurfaceTexture::setTransform");
+    ST_LOGV("setTransform: xform=%#x", transform);
     Mutex::Autolock lock(mMutex);
     if (mAbandoned) {
         ST_LOGE("setTransform: SurfaceTexture has been abandoned!");
@@ -579,7 +589,7 @@
 
 status_t SurfaceTexture::connect(int api,
         uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
-    ST_LOGV("SurfaceTexture::connect(this=%p, %d)", this, api);
+    ST_LOGV("connect: api=%d", api);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -612,7 +622,7 @@
 }
 
 status_t SurfaceTexture::disconnect(int api) {
-    ST_LOGV("SurfaceTexture::disconnect(this=%p, %d)", this, api);
+    ST_LOGV("disconnect: api=%d", api);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -640,6 +650,7 @@
             }
             break;
         default:
+            ST_LOGE("disconnect: unknown API %d", api);
             err = -EINVAL;
             break;
     }
@@ -647,13 +658,14 @@
 }
 
 status_t SurfaceTexture::setScalingMode(int mode) {
-    ST_LOGV("SurfaceTexture::setScalingMode(%d)", mode);
+    ST_LOGV("setScalingMode: mode=%d", mode);
 
     switch (mode) {
         case NATIVE_WINDOW_SCALING_MODE_FREEZE:
         case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
             break;
         default:
+            ST_LOGE("unknown scaling mode: %d", mode);
             return BAD_VALUE;
     }
 
@@ -663,7 +675,7 @@
 }
 
 status_t SurfaceTexture::updateTexImage() {
-    ST_LOGV("SurfaceTexture::updateTexImage");
+    ST_LOGV("updateTexImage");
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -713,6 +725,10 @@
             return -EINVAL;
         }
 
+        ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
+                mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf,
+                mSlots[buf].mGraphicBuffer->handle);
+
         if (mCurrentTexture != INVALID_BUFFER_SLOT) {
             // The current buffer becomes FREE if it was still in the queued
             // state. If it has already been given to the client
@@ -771,7 +787,7 @@
 }
 
 void SurfaceTexture::computeCurrentTransformMatrix() {
-    ST_LOGV("SurfaceTexture::computeCurrentTransformMatrix");
+    ST_LOGV("computeCurrentTransformMatrix");
 
     float xform[16];
     for (int i = 0; i < 16; i++) {
@@ -862,14 +878,14 @@
 }
 
 nsecs_t SurfaceTexture::getTimestamp() {
-    ST_LOGV("SurfaceTexture::getTimestamp");
+    ST_LOGV("getTimestamp");
     Mutex::Autolock lock(mMutex);
     return mCurrentTimestamp;
 }
 
 void SurfaceTexture::setFrameAvailableListener(
         const sp<FrameAvailableListener>& listener) {
-    ST_LOGV("SurfaceTexture::setFrameAvailableListener");
+    ST_LOGV("setFrameAvailableListener");
     Mutex::Autolock lock(mMutex);
     mFrameAvailableListener = listener;
 }
diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp
index 590576a..d38aae9 100644
--- a/libs/utils/BlobCache.cpp
+++ b/libs/utils/BlobCache.cpp
@@ -21,10 +21,20 @@
 #include <string.h>
 
 #include <utils/BlobCache.h>
+#include <utils/Errors.h>
 #include <utils/Log.h>
 
 namespace android {
 
+// BlobCache::Header::mMagicNumber value
+static const uint32_t blobCacheMagic = '_Bb$';
+
+// BlobCache::Header::mBlobCacheVersion value
+static const uint32_t blobCacheVersion = 1;
+
+// BlobCache::Header::mDeviceVersion value
+static const uint32_t blobCacheDeviceVersion = 1;
+
 BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
         mMaxKeySize(maxKeySize),
         mMaxValueSize(maxValueSize),
@@ -67,12 +77,10 @@
         return;
     }
 
-    Mutex::Autolock lock(mMutex);
     sp<Blob> dummyKey(new Blob(key, keySize, false));
     CacheEntry dummyEntry(dummyKey, NULL);
 
     while (true) {
-
         ssize_t index = mCacheEntries.indexOf(dummyEntry);
         if (index < 0) {
             // Create a new cache entry.
@@ -129,7 +137,6 @@
                 keySize, mMaxKeySize);
         return 0;
     }
-    Mutex::Autolock lock(mMutex);
     sp<Blob> dummyKey(new Blob(key, keySize, false));
     CacheEntry dummyEntry(dummyKey, NULL);
     ssize_t index = mCacheEntries.indexOf(dummyEntry);
@@ -152,6 +159,133 @@
     return valueBlobSize;
 }
 
+static inline size_t align4(size_t size) {
+    return (size + 3) & ~3;
+}
+
+size_t BlobCache::getFlattenedSize() const {
+    size_t size = sizeof(Header);
+    for (size_t i = 0; i < mCacheEntries.size(); i++) {
+        const CacheEntry& e(mCacheEntries[i]);
+        sp<Blob> keyBlob = e.getKey();
+        sp<Blob> valueBlob = e.getValue();
+        size = align4(size);
+        size += sizeof(EntryHeader) + keyBlob->getSize() +
+                valueBlob->getSize();
+    }
+    return size;
+}
+
+size_t BlobCache::getFdCount() const {
+    return 0;
+}
+
+status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count)
+        const {
+    if (count != 0) {
+        LOGE("flatten: nonzero fd count: %d", count);
+        return BAD_VALUE;
+    }
+
+    // Write the cache header
+    if (size < sizeof(Header)) {
+        LOGE("flatten: not enough room for cache header");
+        return BAD_VALUE;
+    }
+    Header* header = reinterpret_cast<Header*>(buffer);
+    header->mMagicNumber = blobCacheMagic;
+    header->mBlobCacheVersion = blobCacheVersion;
+    header->mDeviceVersion = blobCacheDeviceVersion;
+    header->mNumEntries = mCacheEntries.size();
+
+    // Write cache entries
+    uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
+    off_t byteOffset = align4(sizeof(Header));
+    for (size_t i = 0; i < mCacheEntries.size(); i++) {
+        const CacheEntry& e(mCacheEntries[i]);
+        sp<Blob> keyBlob = e.getKey();
+        sp<Blob> valueBlob = e.getValue();
+        size_t keySize = keyBlob->getSize();
+        size_t valueSize = valueBlob->getSize();
+
+        size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+        if (byteOffset + entrySize > size) {
+            LOGE("flatten: not enough room for cache entries");
+            return BAD_VALUE;
+        }
+
+        EntryHeader* eheader = reinterpret_cast<EntryHeader*>(
+            &byteBuffer[byteOffset]);
+        eheader->mKeySize = keySize;
+        eheader->mValueSize = valueSize;
+
+        memcpy(eheader->mData, keyBlob->getData(), keySize);
+        memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize);
+
+        byteOffset += align4(entrySize);
+    }
+
+    return OK;
+}
+
+status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[],
+        size_t count) {
+    // All errors should result in the BlobCache being in an empty state.
+    mCacheEntries.clear();
+
+    if (count != 0) {
+        LOGE("unflatten: nonzero fd count: %d", count);
+        return BAD_VALUE;
+    }
+
+    // Read the cache header
+    if (size < sizeof(Header)) {
+        LOGE("unflatten: not enough room for cache header");
+        return BAD_VALUE;
+    }
+    const Header* header = reinterpret_cast<const Header*>(buffer);
+    if (header->mMagicNumber != blobCacheMagic) {
+        LOGE("unflatten: bad magic number: %d", header->mMagicNumber);
+        return BAD_VALUE;
+    }
+    if (header->mBlobCacheVersion != blobCacheVersion ||
+            header->mDeviceVersion != blobCacheDeviceVersion) {
+        // We treat version mismatches as an empty cache.
+        return OK;
+    }
+
+    // Read cache entries
+    const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer);
+    off_t byteOffset = align4(sizeof(Header));
+    size_t numEntries = header->mNumEntries;
+    for (size_t i = 0; i < numEntries; i++) {
+        if (byteOffset + sizeof(EntryHeader) > size) {
+            mCacheEntries.clear();
+            LOGE("unflatten: not enough room for cache entry headers");
+            return BAD_VALUE;
+        }
+
+        const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
+                &byteBuffer[byteOffset]);
+        size_t keySize = eheader->mKeySize;
+        size_t valueSize = eheader->mValueSize;
+        size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+
+        if (byteOffset + entrySize > size) {
+            mCacheEntries.clear();
+            LOGE("unflatten: not enough room for cache entry headers");
+            return BAD_VALUE;
+        }
+
+        const uint8_t* data = eheader->mData;
+        set(data, keySize, data + keySize, valueSize);
+
+        byteOffset += align4(entrySize);
+    }
+
+    return OK;
+}
+
 long int BlobCache::blob_random() {
 #ifdef _WIN32
     return rand();
@@ -179,7 +313,7 @@
         mData(copyData ? malloc(size) : data),
         mSize(size),
         mOwnsData(copyData) {
-    if (copyData) {
+    if (data != NULL && copyData) {
         memcpy(const_cast<void*>(mData), data, size);
     }
 }
diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp
index 653ea5e..b64cc39 100644
--- a/libs/utils/tests/BlobCache_test.cpp
+++ b/libs/utils/tests/BlobCache_test.cpp
@@ -14,9 +14,13 @@
  ** limitations under the License.
  */
 
+#include <fcntl.h>
+#include <stdio.h>
+
 #include <gtest/gtest.h>
 
 #include <utils/BlobCache.h>
+#include <utils/Errors.h>
 
 namespace android {
 
@@ -254,4 +258,164 @@
     ASSERT_EQ(maxEntries/2 + 1, numCached);
 }
 
+class BlobCacheFlattenTest : public BlobCacheTest {
+protected:
+    virtual void SetUp() {
+        BlobCacheTest::SetUp();
+        mBC2 = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
+    }
+
+    virtual void TearDown() {
+        mBC2.clear();
+        BlobCacheTest::TearDown();
+    }
+
+    void roundTrip() {
+        size_t size = mBC->getFlattenedSize();
+        uint8_t* flat = new uint8_t[size];
+        ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+        ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+        delete[] flat;
+    }
+
+    sp<BlobCache> mBC2;
+};
+
+TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    roundTrip();
+    ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
+    ASSERT_EQ('e', buf[0]);
+    ASSERT_EQ('f', buf[1]);
+    ASSERT_EQ('g', buf[2]);
+    ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenFullCache) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    roundTrip();
+
+    // Verify the deserialized cache
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        uint8_t v = 0xee;
+        ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1));
+        ASSERT_EQ(k, v);
+    }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+    delete[] flat;
+
+    // Verify the cache that we just serialized
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        uint8_t v = 0xee;
+        ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1));
+        ASSERT_EQ(k, v);
+    }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    size_t size = mBC->getFlattenedSize() - 1;
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size, NULL, 0));
+    delete[] flat;
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+    flat[1] = ~flat[1];
+
+    // Bad magic should cause an error.
+    ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size, NULL, 0));
+    delete[] flat;
+
+    // The error should cause the unflatten to result in an empty cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+    flat[5] = ~flat[5];
+
+    // Version mismatches shouldn't cause errors, but should not use the
+    // serialized entries
+    ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+    delete[] flat;
+
+    // The version mismatch should cause the unflatten to result in an empty
+    // cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+    flat[10] = ~flat[10];
+
+    // Version mismatches shouldn't cause errors, but should not use the
+    // serialized entries
+    ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+    delete[] flat;
+
+    // The version mismatch should cause the unflatten to result in an empty
+    // cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+
+    // A buffer truncation shouldt cause an error
+    ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1, NULL, 0));
+    delete[] flat;
+
+    // The error should cause the unflatten to result in an empty cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
 } // namespace android
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 3e66a13..5855b63 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -8,6 +8,7 @@
 
 LOCAL_SRC_FILES:= 	       \
 	EGL/egl_tls.cpp        \
+	EGL/egl_cache.cpp      \
 	EGL/egl_display.cpp    \
 	EGL/egl_object.cpp     \
 	EGL/egl.cpp 	       \
@@ -157,4 +158,3 @@
 include $(BUILD_SHARED_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 1f9ce68..60ac34b 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -858,10 +858,17 @@
         return  NULL;
     }
 
+    // The EGL_ANDROID_blob_cache extension should not be exposed to
+    // applications.  It is used internally by the Android EGL layer.
+    if (!strcmp(procname, "eglSetBlobCacheFuncs")) {
+        return NULL;
+    }
+
     __eglMustCastToProperFunctionPointerType addr;
     addr = findProcAddress(procname, sExtentionMap, NELEM(sExtentionMap));
     if (addr) return addr;
 
+
     // this protects accesses to sGLExtentionMap and sGLExtentionSlot
     pthread_mutex_lock(&sExtensionMapMutex);
 
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
new file mode 100644
index 0000000..1e64302
--- /dev/null
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -0,0 +1,95 @@
+/*
+ ** Copyright 2011, 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 "egl_cache.h"
+#include "egl_display.h"
+#include "egl_impl.h"
+#include "egldefs.h"
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+#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
+//
+static void setBlob(const void* key, EGLsizei keySize, const void* value,
+        EGLsizei valueSize) {
+}
+
+static EGLsizei getBlob(const void* key, EGLsizei keySize, void* value,
+        EGLsizei valueSize) {
+    return 0;
+}
+
+egl_cache_t* egl_cache_t::get() {
+    static egl_cache_t theCache;
+    return &theCache;
+}
+
+void egl_cache_t::initialize(egl_display_t *display) {
+    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) {
+            const char* exts = display->disp[i].queryString.extensions;
+            size_t bcExtLen = strlen(BC_EXT_STR);
+            size_t extsLen = strlen(exts);
+            bool equal = !strcmp(BC_EXT_STR, exts);
+            bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
+            bool atEnd = (bcExtLen+1) < extsLen &&
+                    !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
+            bool inMiddle = strstr(" " BC_EXT_STR " ", exts);
+            if (equal || atStart || atEnd || inMiddle) {
+                PFNEGLSETBLOBCACHEFUNCSPROC eglSetBlobCacheFuncs;
+                eglSetBlobCacheFuncs =
+                        reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSPROC>(
+                            cnx->egl.eglGetProcAddress("eglSetBlobCacheFuncs"));
+                if (eglSetBlobCacheFuncs == NULL) {
+                    LOGE("EGL_ANDROID_blob_cache advertised by display %d, "
+                            "but unable to get eglSetBlobCacheFuncs", i);
+                    continue;
+                }
+
+                eglSetBlobCacheFuncs(display->disp[i].dpy, setBlob, getBlob);
+                EGLint err = cnx->egl.eglGetError();
+                if (err != EGL_SUCCESS) {
+                    LOGE("eglSetBlobCacheFuncs resulted in an error: %#x",
+                            err);
+                }
+            }
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
new file mode 100644
index 0000000..1fcfacc
--- /dev/null
+++ b/opengl/libs/EGL/egl_cache.h
@@ -0,0 +1,33 @@
+/*
+ ** Copyright 2011, 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.
+ */
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class egl_display_t;
+
+class egl_cache_t {
+public:
+
+    static egl_cache_t* get();
+
+    void initialize(egl_display_t* display);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 83aafa6..0f92864 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -14,6 +14,7 @@
  ** limitations under the License.
  */
 
+#include "egl_cache.h"
 #include "egl_display.h"
 #include "egl_object.h"
 #include "egl_tls.h"
@@ -170,6 +171,8 @@
         }
     }
 
+    egl_cache_t::get()->initialize(this);
+
     EGLBoolean res = EGL_FALSE;
     for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
         egl_connection_t* const cnx = &gEGLImpl[i];
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 61a8358..b916bd7 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -30,6 +30,10 @@
 	LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE
 endif
 
+ifneq (,$(findstring $(TARGET_DEVICE),tuna toro maguro))
+	LOCAL_CFLAGS += -DREFRESH_RATE=48
+endif
+
 
 LOCAL_SHARED_LIBRARIES := \
 	libcutils \
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index f4be168..329c052 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -141,6 +141,17 @@
     mDpiY = mNativeWindow->ydpi;
     mRefreshRate = fbDev->fps;
 
+
+/* FIXME: this is a temporary HACK until we are able to report the refresh rate
+ * properly from the HAL. The WindowManagerService now relies on this value.
+ */
+#ifndef REFRESH_RATE
+    mRefreshRate = fbDev->fps;
+#else
+    mRefreshRate = REFRESH_RATE;
+#warning "refresh rate set via makefile to REFRESH_RATE"
+#endif
+
     EGLint w, h, dummy;
     EGLint numConfigs=0;
     EGLSurface surface;
diff --git a/services/surfaceflinger/LayerScreenshot.cpp b/services/surfaceflinger/LayerScreenshot.cpp
index e30ccbf..68e6660 100644
--- a/services/surfaceflinger/LayerScreenshot.cpp
+++ b/services/surfaceflinger/LayerScreenshot.cpp
@@ -27,6 +27,7 @@
 #include "SurfaceFlinger.h"
 #include "DisplayHardware/DisplayHardware.h"
 
+
 namespace android {
 // ---------------------------------------------------------------------------
 
@@ -45,23 +46,64 @@
     }
 }
 
+status_t LayerScreenshot::captureLocked() {
+    GLfloat u, v;
+    status_t result = mFlinger->renderScreenToTextureLocked(0, &mTextureName, &u, &v);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    initTexture(u, v);
+    return NO_ERROR;
+}
+
 status_t LayerScreenshot::capture() {
     GLfloat u, v;
     status_t result = mFlinger->renderScreenToTexture(0, &mTextureName, &u, &v);
     if (result != NO_ERROR) {
         return result;
     }
+    initTexture(u, v);
+    return NO_ERROR;
+}
 
+void LayerScreenshot::initTexture(GLfloat u, GLfloat v) {
     glBindTexture(GL_TEXTURE_2D, mTextureName);
     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
     mTexCoords[0] = 0;         mTexCoords[1] = v;
     mTexCoords[2] = 0;         mTexCoords[3] = 0;
     mTexCoords[4] = u;         mTexCoords[5] = 0;
     mTexCoords[6] = u;         mTexCoords[7] = v;
+}
 
-    return NO_ERROR;
+void LayerScreenshot::initStates(uint32_t w, uint32_t h, uint32_t flags) {
+    LayerBaseClient::initStates(w, h, flags);
+    if (!(flags & ISurfaceComposer::eHidden)) {
+        capture();
+    }
+}
+
+uint32_t LayerScreenshot::doTransaction(uint32_t flags)
+{
+    const Layer::State& draw(drawingState());
+    const Layer::State& curr(currentState());
+
+    if (draw.flags & ISurfaceComposer::eLayerHidden) {
+        if (!(curr.flags & ISurfaceComposer::eLayerHidden)) {
+            // we're going from hidden to visible
+            status_t err = captureLocked();
+            if (err != NO_ERROR) {
+                LOGW("createScreenshotSurface failed (%s)", strerror(-err));
+            }
+        }
+    } else if (curr.flags & ISurfaceComposer::eLayerHidden) {
+        // we're going from visible to hidden
+        if (mTextureName) {
+            glDeleteTextures(1, &mTextureName);
+            mTextureName = 0;
+        }
+    }
+    return LayerBaseClient::doTransaction(flags);
 }
 
 void LayerScreenshot::onDraw(const Region& clip) const
diff --git a/services/surfaceflinger/LayerScreenshot.h b/services/surfaceflinger/LayerScreenshot.h
index e3a2b19..ab90047 100644
--- a/services/surfaceflinger/LayerScreenshot.h
+++ b/services/surfaceflinger/LayerScreenshot.h
@@ -41,12 +41,18 @@
 
         status_t capture();
 
+    virtual void initStates(uint32_t w, uint32_t h, uint32_t flags);
+    virtual uint32_t doTransaction(uint32_t flags);
     virtual void onDraw(const Region& clip) const;
     virtual bool isOpaque() const         { return false; }
     virtual bool isSecure() const         { return false; }
     virtual bool isProtectedByApp() const { return false; }
     virtual bool isProtectedByDRM() const { return false; }
     virtual const char* getTypeId() const { return "LayerScreenshot"; }
+
+private:
+    status_t captureLocked();
+    void initTexture(GLfloat u, GLfloat v);
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a6d4147..8f4fdb8 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1360,11 +1360,6 @@
         uint32_t w, uint32_t h, uint32_t flags)
 {
     sp<LayerScreenshot> layer = new LayerScreenshot(this, display, client);
-    status_t err = layer->capture();
-    if (err != NO_ERROR) {
-        layer.clear();
-        LOGW("createScreenshotSurface failed (%s)", strerror(-err));
-    }
     return layer;
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ea5bfa7..17028db 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -186,6 +186,8 @@
 
             status_t renderScreenToTexture(DisplayID dpy,
                     GLuint* textureName, GLfloat* uOut, GLfloat* vOut);
+            status_t renderScreenToTextureLocked(DisplayID dpy,
+                    GLuint* textureName, GLfloat* uOut, GLfloat* vOut);
 
             status_t postMessageAsync(const sp<MessageBase>& msg,
                     nsecs_t reltime=0, uint32_t flags = 0);
@@ -328,8 +330,6 @@
             status_t turnElectronBeamOnImplLocked(int32_t mode);
             status_t electronBeamOffAnimationImplLocked();
             status_t electronBeamOnAnimationImplLocked();
-            status_t renderScreenToTextureLocked(DisplayID dpy,
-                    GLuint* textureName, GLfloat* uOut, GLfloat* vOut);
 
             void        debugFlashRegions();
             void        debugShowFPS() const;