Merge "SensorService performance improvements." into lmp-dev
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index ff01da5..d2f99df 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -20,13 +20,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/wait.h>
 #include <unistd.h>
-#include <sys/capability.h>
-#include <sys/prctl.h>
 
 #include <cutils/properties.h>
 
@@ -45,6 +45,36 @@
 
 #define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
 
+#define TOMBSTONE_DIR "/data/tombstones"
+#define TOMBSTONE_FILE_PREFIX TOMBSTONE_DIR "/tombstone_"
+/* Can accomodate a tombstone number up to 9999. */
+#define TOMBSTONE_MAX_LEN (sizeof(TOMBSTONE_FILE_PREFIX) + 4)
+#define NUM_TOMBSTONES  10
+
+typedef struct {
+  char name[TOMBSTONE_MAX_LEN];
+  int fd;
+} tombstone_data_t;
+
+static tombstone_data_t tombstone_data[NUM_TOMBSTONES];
+
+/* Get the fds of any tombstone that was modified in the last half an hour. */
+static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
+    time_t thirty_minutes_ago = time(NULL) - 60*30;
+    for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
+        snprintf(data[i].name, sizeof(data[i].name), "%s%02zu", TOMBSTONE_FILE_PREFIX, i);
+        int fd = open(data[i].name, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
+        struct stat st;
+        if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) &&
+                (time_t) st.st_mtime >= thirty_minutes_ago) {
+            data[i].fd = fd;
+        } else {
+            close(fd);
+            data[i].fd = -1;
+        }
+    }
+}
+
 /* dumps the current system state to stdout */
 static void dumpstate() {
     time_t now = time(NULL);
@@ -119,7 +149,6 @@
     run_command("EVENT LOG", 20, "logcat", "-b", "events", "-v", "threadtime", "-d", "*:v", NULL);
     run_command("RADIO LOG", 20, "logcat", "-b", "radio", "-v", "threadtime", "-d", "*:v", NULL);
 
-
     /* show the traces we collected in main(), if that was done */
     if (dump_traces_path != NULL) {
         dump_file("VM TRACES JUST NOW", dump_traces_path);
@@ -131,10 +160,13 @@
     property_get("dalvik.vm.stack-trace-file", anr_traces_path, "");
     if (!anr_traces_path[0]) {
         printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
-    } else if (stat(anr_traces_path, &st)) {
-        printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno));
     } else {
-        dump_file("VM TRACES AT LAST ANR", anr_traces_path);
+      int fd = open(anr_traces_path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
+      if (fd < 0) {
+          printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno));
+      } else {
+          dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path, fd);
+      }
     }
 
     /* slow traces for slow operations */
@@ -155,6 +187,18 @@
         }
     }
 
+    int dumped = 0;
+    for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
+        if (tombstone_data[i].fd != -1) {
+            dumped = 1;
+            dump_file_from_fd("TOMBSTONE", tombstone_data[i].name, tombstone_data[i].fd);
+            tombstone_data[i].fd = -1;
+        }
+    }
+    if (!dumped) {
+        printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR);
+    }
+
     dump_file("NETWORK DEV INFO", "/proc/net/dev");
     dump_file("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
     dump_file("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
@@ -411,6 +455,9 @@
         return -1;
     }
 
+    /* Get the tombstone fds here while we are running as root. */
+    get_tombstone_fds(tombstone_data);
+
     /* switch to non-root user and group */
     gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW,
             AID_MOUNT, AID_INET, AID_NET_BW_STATS };
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 111bda6..53bfff6 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -29,7 +29,10 @@
 typedef void (for_each_userid_func)(int);
 
 /* prints the contents of a file */
-int dump_file(const char *title, const char* path);
+int dump_file(const char *title, const char *path);
+
+/* prints the contents of the fd */
+int dump_file_from_fd(const char *title, const char *path, int fd);
 
 /* forks a command and waits for it to finish -- terminate args with NULL */
 int run_command(const char *title, int timeout_seconds, const char *command, ...);
@@ -71,7 +74,7 @@
 void dump_route_tables();
 
 /* Play a sound via Stagefright */
-void play_sound(const char* path);
+void play_sound(const char *path);
 
 /* Implemented by libdumpstate_board to dump board-specific info */
 void dumpstate_board();
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
index 7694adb..a6d9ef6 100644
--- a/cmds/dumpstate/utils.c
+++ b/cmds/dumpstate/utils.c
@@ -249,8 +249,7 @@
 }
 
 /* prints the contents of a file */
-int dump_file(const char *title, const char* path) {
-    char buffer[32768];
+int dump_file(const char *title, const char *path) {
     int fd = open(path, O_RDONLY);
     if (fd < 0) {
         int err = errno;
@@ -259,6 +258,11 @@
         if (title) printf("\n");
         return -1;
     }
+    return dump_file_from_fd(title, path, fd);
+}
+
+int dump_file_from_fd(const char *title, const char *path, int fd) {
+    char buffer[32768];
 
     if (title) printf("------ %s (%s", title, path);
 
@@ -282,8 +286,8 @@
         }
         if (ret <= 0) break;
     }
-
     close(fd);
+
     if (!newline) printf("\n");
     if (title) printf("\n");
     return 0;
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 1aacee9..37530db 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -231,7 +231,7 @@
 protected:
 
     // abandonLocked overrides the ConsumerBase method to clear
-    // mCurrentTextureBuf in addition to the ConsumerBase behavior.
+    // mCurrentTextureImage in addition to the ConsumerBase behavior.
     virtual void abandonLocked();
 
     // dumpLocked overrides the ConsumerBase method to dump GLConsumer-
@@ -262,7 +262,7 @@
     status_t updateAndReleaseLocked(const BufferQueue::BufferItem& item);
 
     // Binds mTexName and the current buffer to mTexTarget.  Uses
-    // mCurrentTexture if it's set, mCurrentTextureBuf if not.  If the
+    // mCurrentTexture if it's set, mCurrentTextureImage if not.  If the
     // bind succeeds, this calls doGLFenceWait.
     status_t bindTextureImageLocked();
 
@@ -275,11 +275,57 @@
     status_t checkAndUpdateEglStateLocked(bool contextCheck = false);
 
 private:
-    // createImage creates a new EGLImage from a GraphicBuffer.
-    EGLImageKHR createImage(EGLDisplay dpy,
-            const sp<GraphicBuffer>& graphicBuffer, const Rect& crop);
+    // EglImage is a utility class for tracking and creating EGLImageKHRs. There
+    // is primarily just one image per slot, but there is also special cases:
+    //  - For releaseTexImage, we use a debug image (mReleasedTexImage)
+    //  - After freeBuffer, we must still keep the current image/buffer
+    // Reference counting EGLImages lets us handle all these cases easily while
+    // also only creating new EGLImages from buffers when required.
+    class EglImage : public LightRefBase<EglImage>  {
+    public:
+        EglImage(sp<GraphicBuffer> graphicBuffer);
 
-    // freeBufferLocked frees up the given buffer slot.  If the slot has been
+        // createIfNeeded creates an EGLImage if required (we haven't created
+        // one yet, or the EGLDisplay or crop-rect has changed).
+        status_t createIfNeeded(EGLDisplay display, const Rect& cropRect);
+
+        // This calls glEGLImageTargetTexture2DOES to bind the image to the
+        // texture in the specified texture target.
+        void bindToTextureTarget(uint32_t texTarget);
+
+        const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
+        const native_handle* graphicBufferHandle() {
+            return mGraphicBuffer == NULL ? NULL : mGraphicBuffer->handle;
+        }
+
+    private:
+        // Only allow instantiation using ref counting.
+        friend class LightRefBase<EglImage>;
+        virtual ~EglImage();
+
+        // createImage creates a new EGLImage from a GraphicBuffer.
+        EGLImageKHR createImage(EGLDisplay dpy,
+                const sp<GraphicBuffer>& graphicBuffer, const Rect& crop);
+
+        // Disallow copying
+        EglImage(const EglImage& rhs);
+        void operator = (const EglImage& rhs);
+
+        // mGraphicBuffer is the buffer that was used to create this image.
+        sp<GraphicBuffer> mGraphicBuffer;
+
+        // mEglImage is the EGLImage created from mGraphicBuffer.
+        EGLImageKHR mEglImage;
+
+        // mEGLDisplay is the EGLDisplay that was used to create mEglImage.
+        EGLDisplay mEglDisplay;
+
+        // mCropRect is the crop rectangle passed to EGL when mEglImage
+        // was created.
+        Rect mCropRect;
+    };
+
+    // freeBufferLocked frees up the given buffer slot. If the slot has been
     // initialized this will release the reference to the GraphicBuffer in that
     // slot and destroy the EGLImage in that slot.  Otherwise it has no effect.
     //
@@ -289,7 +335,7 @@
     // computeCurrentTransformMatrixLocked computes the transform matrix for the
     // current texture.  It uses mCurrentTransform and the current GraphicBuffer
     // to compute this matrix and stores it in mCurrentTransformMatrix.
-    // mCurrentTextureBuf must not be NULL.
+    // mCurrentTextureImage must not be NULL.
     void computeCurrentTransformMatrixLocked();
 
     // doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
@@ -303,13 +349,6 @@
     // before the outstanding accesses have completed.
     status_t syncForReleaseLocked(EGLDisplay dpy);
 
-    // Normally, when we bind a buffer to a texture target, we bind a buffer
-    // that is referenced by an entry in mEglSlots.  In some situations we
-    // have a buffer in mCurrentTextureBuf, but no corresponding entry for
-    // it in our slot array.  bindUnslottedBuffer handles that situation by
-    // binding the buffer without touching the EglSlots.
-    status_t bindUnslottedBufferLocked(EGLDisplay dpy);
-
     // returns a graphic buffer used when the texture image has been released
     static sp<GraphicBuffer> getDebugTexImageBuffer();
 
@@ -319,10 +358,10 @@
     // consume buffers as hardware textures.
     static const uint32_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
 
-    // mCurrentTextureBuf is the graphic buffer of the current texture. It's
+    // mCurrentTextureImage is the EglImage/buffer of the current texture. It's
     // possible that this buffer is not associated with any buffer slot, so we
     // must track it separately in order to support the getCurrentBuffer method.
-    sp<GraphicBuffer> mCurrentTextureBuf;
+    sp<EglImage> mCurrentTextureImage;
 
     // mCurrentCrop is the crop rectangle that applies to the current texture.
     // It gets set each time updateTexImage is called.
@@ -382,17 +421,10 @@
     // EGLSlot contains the information and object references that
     // GLConsumer maintains about a BufferQueue buffer slot.
     struct EglSlot {
-        EglSlot()
-        : mEglImage(EGL_NO_IMAGE_KHR),
-          mEglFence(EGL_NO_SYNC_KHR) {
-        }
+        EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
 
         // mEglImage is the EGLImage created from mGraphicBuffer.
-        EGLImageKHR mEglImage;
-
-        // mCropRect is the crop rectangle passed to EGL when mEglImage was
-        // created.
-        Rect mCropRect;
+        sp<EglImage> mEglImage;
 
         // mFence is the EGL sync object that must signal before the buffer
         // associated with this buffer slot may be dequeued. It is initialized
@@ -444,6 +476,7 @@
     // mReleasedTexImageBuffer is a dummy buffer used when in single buffer
     // mode and releaseTexImage() has been called
     static sp<GraphicBuffer> sReleasedTexImageBuffer;
+    sp<EglImage> mReleasedTexImage;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 924fc0d..939c4a9 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -279,8 +279,12 @@
             return err;
         }
 
+        if (mReleasedTexImage == NULL) {
+            mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
+        }
+
         mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
-        mCurrentTextureBuf = getDebugTexImageBuffer();
+        mCurrentTextureImage = mReleasedTexImage;
         mCurrentCrop.makeInvalid();
         mCurrentTransform = 0;
         mCurrentScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
@@ -288,9 +292,11 @@
         mCurrentFence = Fence::NO_FENCE;
 
         if (mAttached) {
-            // bind a dummy texture
-            glBindTexture(mTexTarget, mTexName);
-            bindUnslottedBufferLocked(mEglDisplay);
+            // This binds a dummy buffer (mReleasedTexImage).
+            status_t err =  bindTextureImageLocked();
+            if (err != NO_ERROR) {
+                return err;
+            }
         } else {
             // detached, don't touch the texture (and we may not even have an
             // EGLDisplay here.
@@ -332,29 +338,12 @@
         return err;
     }
 
-    int slot = item->mBuf;
-    bool destroyEglImage = false;
-
-    if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
-        if (item->mGraphicBuffer != NULL) {
-            // This buffer has not been acquired before, so we must assume
-            // that any EGLImage in mEglSlots is stale.
-            destroyEglImage = true;
-        } else if (mEglSlots[slot].mCropRect != item->mCrop) {
-            // We've already seen this buffer before, but it now has a
-            // different crop rect, so we'll need to recreate the EGLImage if
-            // we're using the EGL_ANDROID_image_crop extension.
-            destroyEglImage = hasEglAndroidImageCrop();
-        }
-    }
-
-    if (destroyEglImage) {
-        if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) {
-            ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d",
-                  slot);
-            // keep going
-        }
-        mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
+    // If item->mGraphicBuffer is not null, this buffer has not been acquired
+    // before, so any prior EglImage created is using a stale buffer. This
+    // replaces any old EglImage with a new one (using the new buffer).
+    if (item->mGraphicBuffer != NULL) {
+        int slot = item->mBuf;
+        mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer);
     }
 
     return NO_ERROR;
@@ -395,29 +384,18 @@
         return err;
     }
 
-    // If the mEglSlot entry is empty, create an EGLImage for the gralloc
-    // buffer currently in the slot in ConsumerBase.
-    //
+    // Ensure we have a valid EglImageKHR for the slot, creating an EglImage
+    // if nessessary, for the gralloc buffer currently in the slot in
+    // ConsumerBase.
     // We may have to do this even when item.mGraphicBuffer == NULL (which
-    // means the buffer was previously acquired), if we destroyed the
-    // EGLImage when detaching from a context but the buffer has not been
-    // re-allocated.
-    if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) {
-        EGLImageKHR image = createImage(mEglDisplay,
-                mSlots[buf].mGraphicBuffer, item.mCrop);
-        if (image == EGL_NO_IMAGE_KHR) {
-            ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
-                  mEglDisplay, buf);
-            const sp<GraphicBuffer>& gb = mSlots[buf].mGraphicBuffer;
-            ST_LOGW("buffer size=%ux%u st=%u usage=0x%x fmt=%d",
-                gb->getWidth(), gb->getHeight(), gb->getStride(),
-                gb->getUsage(), gb->getPixelFormat());
-            releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
-                    mEglDisplay, EGL_NO_SYNC_KHR);
-            return UNKNOWN_ERROR;
-        }
-        mEglSlots[buf].mEglImage = image;
-        mEglSlots[buf].mCropRect = item.mCrop;
+    // means the buffer was previously acquired).
+    err = mEglSlots[buf].mEglImage->createIfNeeded(mEglDisplay, item.mCrop);
+    if (err != NO_ERROR) {
+        ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
+                mEglDisplay, buf);
+        releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
+                mEglDisplay, EGL_NO_SYNC_KHR);
+        return UNKNOWN_ERROR;
     }
 
     // Do whatever sync ops we need to do before releasing the old slot.
@@ -433,15 +411,15 @@
     }
 
     ST_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)",
-            mCurrentTexture,
-            mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
+            mCurrentTexture, mCurrentTextureImage != NULL ?
+                    mCurrentTextureImage->graphicBufferHandle() : 0,
             buf, mSlots[buf].mGraphicBuffer->handle);
 
     // release old buffer
     if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
         status_t status = releaseBufferLocked(
-                mCurrentTexture, mCurrentTextureBuf, mEglDisplay,
-                mEglSlots[mCurrentTexture].mEglFence);
+                mCurrentTexture, mCurrentTextureImage->graphicBuffer(),
+                mEglDisplay, mEglSlots[mCurrentTexture].mEglFence);
         if (status < NO_ERROR) {
             ST_LOGE("updateAndRelease: failed to release buffer: %s (%d)",
                    strerror(-status), status);
@@ -452,7 +430,7 @@
 
     // Update the GLConsumer state.
     mCurrentTexture = buf;
-    mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
+    mCurrentTextureImage = mEglSlots[buf].mEglImage;
     mCurrentCrop = item.mCrop;
     mCurrentTransform = item.mTransform;
     mCurrentScalingMode = item.mScalingMode;
@@ -477,25 +455,26 @@
     }
 
     glBindTexture(mTexTarget, mTexName);
-    if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
-        if (mCurrentTextureBuf == NULL) {
-            ST_LOGE("bindTextureImage: no currently-bound texture");
-            return NO_INIT;
-        }
-        status_t err = bindUnslottedBufferLocked(mEglDisplay);
-        if (err != NO_ERROR) {
-            return err;
-        }
-    } else {
-        EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage;
+    if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT &&
+            mCurrentTextureImage == NULL) {
+        ST_LOGE("bindTextureImage: no currently-bound texture");
+        return NO_INIT;
+    }
 
-        glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
+    status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay,
+                                                      mCurrentCrop);
 
-        while ((error = glGetError()) != GL_NO_ERROR) {
-            ST_LOGE("bindTextureImage: error binding external texture image %p"
-                    ": %#04x", image, error);
-            return UNKNOWN_ERROR;
-        }
+    if (err != NO_ERROR) {
+        ST_LOGW("bindTextureImage: can't create image on display=%p slot=%d",
+                mEglDisplay, mCurrentTexture);
+        return UNKNOWN_ERROR;
+    }
+
+    mCurrentTextureImage->bindToTextureTarget(mTexTarget);
+
+    while ((error = glGetError()) != GL_NO_ERROR) {
+        ST_LOGE("bindTextureImage: error binding external image: %#04x", error);
+        return UNKNOWN_ERROR;
     }
 
     // Wait for the new buffer to be ready.
@@ -537,7 +516,7 @@
     if (fence->isValid() &&
             mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
         status_t err = addReleaseFence(mCurrentTexture,
-                mCurrentTextureBuf, fence);
+                mCurrentTextureImage->graphicBuffer(), fence);
         if (err != OK) {
             ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)",
                     strerror(-err), err);
@@ -583,18 +562,6 @@
         glDeleteTextures(1, &mTexName);
     }
 
-    // Because we're giving up the EGLDisplay we need to free all the EGLImages
-    // that are associated with it.  They'll be recreated when the
-    // GLConsumer gets attached to a new OpenGL ES context (and thus gets a
-    // new EGLDisplay).
-    for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-        EGLImageKHR img = mEglSlots[i].mEglImage;
-        if (img != EGL_NO_IMAGE_KHR) {
-            eglDestroyImageKHR(mEglDisplay, img);
-            mEglSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
-        }
-    }
-
     mEglDisplay = EGL_NO_DISPLAY;
     mEglContext = EGL_NO_CONTEXT;
     mAttached = false;
@@ -635,56 +602,25 @@
     // buffer.
     glBindTexture(mTexTarget, GLuint(tex));
 
-    if (mCurrentTextureBuf != NULL) {
-        // The EGLImageKHR that was associated with the slot was destroyed when
-        // the GLConsumer was detached from the old context, so we need to
-        // recreate it here.
-        status_t err = bindUnslottedBufferLocked(dpy);
-        if (err != NO_ERROR) {
-            return err;
-        }
-    }
-
     mEglDisplay = dpy;
     mEglContext = ctx;
     mTexName = tex;
     mAttached = true;
 
+    if (mCurrentTextureImage != NULL) {
+        // This may wait for a buffer a second time. This is likely required if
+        // this is a different context, since otherwise the wait could be skipped
+        // by bouncing through another context. For the same context the extra
+        // wait is redundant.
+        status_t err =  bindTextureImageLocked();
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
     return OK;
 }
 
-status_t GLConsumer::bindUnslottedBufferLocked(EGLDisplay dpy) {
-    ST_LOGV("bindUnslottedBuffer ct=%d ctb=%p",
-            mCurrentTexture, mCurrentTextureBuf.get());
-
-    // Create a temporary EGLImageKHR.
-    Rect crop;
-    EGLImageKHR image = createImage(dpy, mCurrentTextureBuf, mCurrentCrop);
-    if (image == EGL_NO_IMAGE_KHR) {
-        return UNKNOWN_ERROR;
-    }
-
-    // Attach the current buffer to the GL texture.
-    glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
-
-    GLint error;
-    status_t err = OK;
-    while ((error = glGetError()) != GL_NO_ERROR) {
-        ST_LOGE("bindUnslottedBuffer: error binding external texture image %p "
-                "(slot %d): %#04x", image, mCurrentTexture, error);
-        err = UNKNOWN_ERROR;
-    }
-
-    // We destroy the EGLImageKHR here because the current buffer may no
-    // longer be associated with one of the buffer slots, so we have
-    // nowhere to to store it.  If the buffer is still associated with a
-    // slot then another EGLImageKHR will be created next time that buffer
-    // gets acquired in updateTexImage.
-    eglDestroyImageKHR(dpy, image);
-
-    return err;
-}
-
 
 status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) {
     ST_LOGV("syncForReleaseLocked");
@@ -708,7 +644,7 @@
             }
             sp<Fence> fence(new Fence(fenceFd));
             status_t err = addReleaseFenceLocked(mCurrentTexture,
-                    mCurrentTextureBuf, fence);
+                    mCurrentTextureImage->graphicBuffer(), fence);
             if (err != OK) {
                 ST_LOGE("syncForReleaseLocked: error adding release fence: "
                         "%s (%d)", strerror(-err), err);
@@ -787,11 +723,11 @@
     bool needsRecompute = mFilteringEnabled != enabled;
     mFilteringEnabled = enabled;
 
-    if (needsRecompute && mCurrentTextureBuf==NULL) {
-        ST_LOGD("setFilteringEnabled called with mCurrentTextureBuf == NULL");
+    if (needsRecompute && mCurrentTextureImage==NULL) {
+        ST_LOGD("setFilteringEnabled called with mCurrentTextureImage == NULL");
     }
 
-    if (needsRecompute && mCurrentTextureBuf != NULL) {
+    if (needsRecompute && mCurrentTextureImage != NULL) {
         computeCurrentTransformMatrixLocked();
     }
 }
@@ -825,10 +761,11 @@
         }
     }
 
-    sp<GraphicBuffer>& buf(mCurrentTextureBuf);
+    sp<GraphicBuffer> buf = (mCurrentTextureImage == NULL) ?
+            NULL : mCurrentTextureImage->graphicBuffer();
 
     if (buf == NULL) {
-        ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL");
+        ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureImage is NULL");
     }
 
     float mtxBeforeFlipV[16];
@@ -911,39 +848,10 @@
     return mCurrentFrameNumber;
 }
 
-EGLImageKHR GLConsumer::createImage(EGLDisplay dpy,
-        const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) {
-    EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
-    EGLint attrs[] = {
-        EGL_IMAGE_PRESERVED_KHR,        EGL_TRUE,
-        EGL_IMAGE_CROP_LEFT_ANDROID,    crop.left,
-        EGL_IMAGE_CROP_TOP_ANDROID,     crop.top,
-        EGL_IMAGE_CROP_RIGHT_ANDROID,   crop.right,
-        EGL_IMAGE_CROP_BOTTOM_ANDROID,  crop.bottom,
-        EGL_NONE,
-    };
-    if (!crop.isValid()) {
-        // No crop rect to set, so terminate the attrib array before the crop.
-        attrs[2] = EGL_NONE;
-    } else if (!isEglImageCroppable(crop)) {
-        // The crop rect is not at the origin, so we can't set the crop on the
-        // EGLImage because that's not allowed by the EGL_ANDROID_image_crop
-        // extension.  In the future we can add a layered extension that
-        // removes this restriction if there is hardware that can support it.
-        attrs[2] = EGL_NONE;
-    }
-    EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
-            EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
-    if (image == EGL_NO_IMAGE_KHR) {
-        EGLint error = eglGetError();
-        ST_LOGE("error creating EGLImage: %#x", error);
-    }
-    return image;
-}
-
 sp<GraphicBuffer> GLConsumer::getCurrentBuffer() const {
     Mutex::Autolock lock(mMutex);
-    return mCurrentTextureBuf;
+    return (mCurrentTextureImage == NULL) ?
+            NULL : mCurrentTextureImage->graphicBuffer();
 }
 
 Rect GLConsumer::getCurrentCrop() const {
@@ -1067,18 +975,13 @@
     if (slotIndex == mCurrentTexture) {
         mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
     }
-    EGLImageKHR img = mEglSlots[slotIndex].mEglImage;
-    if (img != EGL_NO_IMAGE_KHR) {
-        ST_LOGV("destroying EGLImage dpy=%p img=%p", mEglDisplay, img);
-        eglDestroyImageKHR(mEglDisplay, img);
-    }
-    mEglSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
+    mEglSlots[slotIndex].mEglImage.clear();
     ConsumerBase::freeBufferLocked(slotIndex);
 }
 
 void GLConsumer::abandonLocked() {
     ST_LOGV("abandonLocked");
-    mCurrentTextureBuf.clear();
+    mCurrentTextureImage.clear();
     ConsumerBase::abandonLocked();
 }
 
@@ -1138,4 +1041,87 @@
     out[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15];
 }
 
+GLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer) :
+    mGraphicBuffer(graphicBuffer),
+    mEglImage(EGL_NO_IMAGE_KHR),
+    mEglDisplay(EGL_NO_DISPLAY) {
+}
+
+GLConsumer::EglImage::~EglImage() {
+    if (mEglImage != EGL_NO_IMAGE_KHR) {
+        if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+           ALOGE("~EglImage: eglDestroyImageKHR failed");
+        }
+    }
+}
+
+status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay,
+                                              const Rect& cropRect) {
+    // If there's an image and it's no longer valid, destroy it.
+    bool haveImage = mEglImage != EGL_NO_IMAGE_KHR;
+    bool displayInvalid = mEglDisplay != eglDisplay;
+    bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect;
+    if (haveImage && (displayInvalid || cropInvalid)) {
+        if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+           ALOGE("createIfNeeded: eglDestroyImageKHR failed");
+        }
+        mEglImage = EGL_NO_IMAGE_KHR;
+        mEglDisplay = EGL_NO_DISPLAY;
+    }
+
+    // If there's no image, create one.
+    if (mEglImage == EGL_NO_IMAGE_KHR) {
+        mEglDisplay = eglDisplay;
+        mCropRect = cropRect;
+        mEglImage = createImage(mEglDisplay, mGraphicBuffer, mCropRect);
+    }
+
+    // Fail if we can't create a valid image.
+    if (mEglImage == EGL_NO_IMAGE_KHR) {
+        mEglDisplay = EGL_NO_DISPLAY;
+        mCropRect.makeInvalid();
+        const sp<GraphicBuffer>& buffer = mGraphicBuffer;
+        ALOGE("Failed to create image. size=%ux%u st=%u usage=0x%x fmt=%d",
+            buffer->getWidth(), buffer->getHeight(), buffer->getStride(),
+            buffer->getUsage(), buffer->getPixelFormat());
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+void GLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) {
+    glEGLImageTargetTexture2DOES(texTarget, (GLeglImageOES)mEglImage);
+}
+
+EGLImageKHR GLConsumer::EglImage::createImage(EGLDisplay dpy,
+        const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) {
+    EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
+    EGLint attrs[] = {
+        EGL_IMAGE_PRESERVED_KHR,        EGL_TRUE,
+        EGL_IMAGE_CROP_LEFT_ANDROID,    crop.left,
+        EGL_IMAGE_CROP_TOP_ANDROID,     crop.top,
+        EGL_IMAGE_CROP_RIGHT_ANDROID,   crop.right,
+        EGL_IMAGE_CROP_BOTTOM_ANDROID,  crop.bottom,
+        EGL_NONE,
+    };
+    if (!crop.isValid()) {
+        // No crop rect to set, so terminate the attrib array before the crop.
+        attrs[2] = EGL_NONE;
+    } else if (!isEglImageCroppable(crop)) {
+        // The crop rect is not at the origin, so we can't set the crop on the
+        // EGLImage because that's not allowed by the EGL_ANDROID_image_crop
+        // extension.  In the future we can add a layered extension that
+        // removes this restriction if there is hardware that can support it.
+        attrs[2] = EGL_NONE;
+    }
+    EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
+            EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
+    if (image == EGL_NO_IMAGE_KHR) {
+        EGLint error = eglGetError();
+        ALOGE("error creating EGLImage: %#x", error);
+    }
+    return image;
+}
+
 }; // namespace android
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 6b90243..cc5d544 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -112,21 +112,16 @@
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 LOCAL_CFLAGS += -fvisibility=hidden
 
-include $(BUILD_SHARED_LIBRARY)
-
 # Symlink libGLESv3.so -> libGLESv2.so
 # Platform modules should link against libGLESv2.so (-lGLESv2), but NDK apps
 # will be linked against libGLESv3.so.
-LIBGLESV2 := $(LOCAL_INSTALLED_MODULE)
-LIBGLESV3 := $(subst libGLESv2,libGLESv3,$(LIBGLESV2))
-$(LIBGLESV3): $(LIBGLESV2)
-	@echo "Symlink: $@ -> $(notdir $<)"
-	@mkdir -p $(dir $@)
-	$(hide) ln -sf $(notdir $<) $@
-ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \
-	$(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(LIBGLESV3)
-LIBGLESV2 :=
-LIBGLESV3 :=
+# Note we defer the evaluation of the LOCAL_POST_INSTALL_CMD,
+# so $(LOCAL_INSTALLED_MODULE) will be expanded to correct value,
+# even for both 32-bit and 64-bit installed files in multilib build.
+LOCAL_POST_INSTALL_CMD = \
+    $(hide) ln -sf $(notdir $(LOCAL_INSTALLED_MODULE)) $(dir $(LOCAL_INSTALLED_MODULE))libGLESv3.so
+
+include $(BUILD_SHARED_LIBRARY)
 
 ###############################################################################
 # Build the ETC1 host static library