Merge "Terminate EGL when an app goes in the background" into ics-mr1
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index c934c7b..d948ec2 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -315,6 +315,27 @@
 
     private static native void nFlushCaches(int level);
 
+    /**
+     * Release all resources associated with the underlying caches. This should
+     * only be called after a full flushCaches().
+     * 
+     * @hide
+     */
+    public static void terminateCaches() {
+        nTerminateCaches();
+    }
+
+    private static native void nTerminateCaches();
+
+    /**
+     * @hide
+     */
+    public static void initCaches() {
+        nInitCaches();
+    }
+
+    private static native void nInitCaches();
+    
     ///////////////////////////////////////////////////////////////////////////
     // Display list
     ///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index c2ac79d..e0167d8 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -25,6 +25,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.util.Log;
+import com.google.android.gles_jni.EGLImpl;
 
 import javax.microedition.khronos.egl.EGL10;
 import javax.microedition.khronos.egl.EGL11;
@@ -344,6 +345,15 @@
     }
 
     /**
+     * Invoke this method when the system needs to clean up all resources
+     * associated with hardware rendering.
+     */
+    static void terminate() {
+        Log.d(LOG_TAG, "Terminating hardware rendering");
+        Gl20Renderer.terminate();
+    }    
+    
+    /**
      * Indicates whether hardware acceleration is currently enabled.
      * 
      * @return True if hardware acceleration is in use, false otherwise.
@@ -651,6 +661,8 @@
                 throw new Surface.OutOfResourcesException("eglMakeCurrent failed "
                         + GLUtils.getEGLErrorString(sEgl.eglGetError()));
             }
+            
+            initCaches();
 
             // If mDirtyRegions is set, this means we have an EGL configuration
             // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
@@ -671,6 +683,8 @@
             return mEglContext.getGL();
         }
 
+        abstract void initCaches();
+
         EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
             int[] attribs = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE };
 
@@ -914,6 +928,11 @@
                     EGL_NONE
             };
         }
+        
+        @Override
+        void initCaches() {
+            GLES20Canvas.initCaches();
+        }
 
         @Override
         boolean canDraw() {
@@ -1006,16 +1025,7 @@
             if (eglContext == null) {
                 return;
             } else {
-                synchronized (sPbufferLock) {
-                    // Create a temporary 1x1 pbuffer so we have a context
-                    // to clear our OpenGL objects
-                    if (sPbuffer == null) {
-                        sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
-                                EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
-                        });
-                    }
-                }
-                sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
+                usePbufferSurface(eglContext);
             }
 
             switch (level) {
@@ -1029,5 +1039,46 @@
                     break;
             }
         }
+
+        private static void usePbufferSurface(EGLContext eglContext) {
+            synchronized (sPbufferLock) {
+                // Create a temporary 1x1 pbuffer so we have a context
+                // to clear our OpenGL objects
+                if (sPbuffer == null) {
+                    sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
+                            EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
+                    });
+                }
+            }
+            sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
+        }
+
+        static void terminate() {
+            synchronized (sEglLock) {
+                if (sEgl == null) return;
+    
+                if (EGLImpl.getInitCount(sEglDisplay) == 1) {
+                    EGLContext eglContext = sEglContextStorage.get();
+                    if (eglContext == null) return;
+
+                    usePbufferSurface(eglContext);
+                    GLES20Canvas.terminateCaches();
+
+                    sEgl.eglDestroyContext(sEglDisplay, eglContext);
+                    sEglContextStorage.remove();
+        
+                    sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
+                    sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+                    sEgl.eglReleaseThread();
+                    sEgl.eglTerminate(sEglDisplay);
+                    
+                    sEgl = null;
+                    sEglDisplay = null;
+                    sEglConfig = null;
+                    sPbuffer = null;
+                }
+            }
+        }
     }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f7078ec..b15b155 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -567,7 +567,7 @@
         }
     }
 
-    private void destroyHardwareResources() {
+    void destroyHardwareResources() {
         if (mAttachInfo.mHardwareRenderer != null) {
             if (mAttachInfo.mHardwareRenderer.isEnabled()) {
                 mAttachInfo.mHardwareRenderer.destroyLayers(mView);
@@ -880,12 +880,10 @@
                 || mNewSurfaceNeeded;
 
         WindowManager.LayoutParams params = null;
-        int windowAttributesChanges = 0;
         if (mWindowAttributesChanged) {
             mWindowAttributesChanged = false;
             surfaceChanged = true;
             params = lp;
-            windowAttributesChanges = mWindowAttributesChangesFlag;
         }
         CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
         if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 5ef4f3e..d89bc36 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.app.ActivityManager;
+import android.content.ComponentCallbacks2;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.PixelFormat;
@@ -409,7 +411,30 @@
      */
     public void trimMemory(int level) {
         if (HardwareRenderer.isAvailable()) {
-            HardwareRenderer.trimMemory(level);
+            switch (level) {
+                case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
+                case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
+                    // On low and medium end gfx devices
+                    if (!ActivityManager.isHighEndGfx(getDefaultDisplay())) {
+                        // Force a full memory flush
+                        HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
+                        // Destroy all hardware surfaces and resources associated to
+                        // known windows
+                        synchronized (this) {
+                            if (mViews == null) return;
+                            int count = mViews.length;
+                            for (int i = 0; i < count; i++) {
+                                mRoots[i].destroyHardwareResources();
+                            }
+                        }
+                        // Terminate the hardware renderer to free all resources
+                        HardwareRenderer.terminate();                        
+                        break;
+                    }
+                    // high end gfx devices fall through to next case
+                default:
+                    HardwareRenderer.trimMemory(level);
+            }
         }
     }
 
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index e79de2d..4f75fad 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -134,6 +134,18 @@
     }
 }
 
+static void android_view_GLES20Canvas_initCaches(JNIEnv* env, jobject clazz) {
+    if (Caches::hasInstance()) {
+        Caches::getInstance().init();
+    }
+}
+
+static void android_view_GLES20Canvas_terminateCaches(JNIEnv* env, jobject clazz) {
+    if (Caches::hasInstance()) {
+        Caches::getInstance().terminate();
+    }
+}
+
 // ----------------------------------------------------------------------------
 // Constructors
 // ----------------------------------------------------------------------------
@@ -756,6 +768,8 @@
     { "nPreserveBackBuffer",    "()Z",         (void*) android_view_GLES20Canvas_preserveBackBuffer },
     { "nDisableVsync",          "()V",         (void*) android_view_GLES20Canvas_disableVsync },
     { "nFlushCaches",           "(I)V",        (void*) android_view_GLES20Canvas_flushCaches },
+    { "nInitCaches",            "()V",         (void*) android_view_GLES20Canvas_initCaches },
+    { "nTerminateCaches",       "()V",         (void*) android_view_GLES20Canvas_terminateCaches },
 
     { "nCreateRenderer",    "()I",             (void*) android_view_GLES20Canvas_createRenderer },
     { "nDestroyRenderer",   "(I)V",            (void*) android_view_GLES20Canvas_destroyRenderer },
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 02974f9a..4fe7600 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -24,6 +24,8 @@
 #include <EGL/egl.h>
 #include <GLES/gl.h>
 
+#include <EGL/egl_display.h>
+
 #include <surfaceflinger/Surface.h>
 #include <SkBitmap.h>
 #include <SkPixelRef.h>
@@ -173,6 +175,16 @@
     return success;
 }
 
+static jint jni_getInitCount(JNIEnv *_env, jobject _clazz, jobject display) {
+    EGLDisplay dpy = getDisplay(_env, display);
+    egl_display_t* eglDisplay = get_display(dpy); 
+    return eglDisplay ? eglDisplay->getRefsCount() : 0;
+}
+
+static jboolean jni_eglReleaseThread(JNIEnv *_env, jobject _this) {
+    return eglReleaseThread();
+}
+
 static jboolean jni_eglChooseConfig(JNIEnv *_env, jobject _this, jobject display,
         jintArray attrib_list, jobjectArray configs, jint config_size, jintArray num_config) {
     if (display == NULL
@@ -526,6 +538,8 @@
 {"eglInitialize",   "(" DISPLAY "[I)Z", (void*)jni_eglInitialize },
 {"eglQueryContext", "(" DISPLAY CONTEXT "I[I)Z", (void*)jni_eglQueryContext },
 {"eglQuerySurface", "(" DISPLAY SURFACE "I[I)Z", (void*)jni_eglQuerySurface },
+{"eglReleaseThread","()Z", (void*)jni_eglReleaseThread },
+{"getInitCount",    "(" DISPLAY ")I", (void*)jni_getInitCount },
 {"eglChooseConfig", "(" DISPLAY "[I[" CONFIG "I[I)Z", (void*)jni_eglChooseConfig },
 {"_eglCreateContext","(" DISPLAY CONFIG CONTEXT "[I)I", (void*)jni_eglCreateContext },
 {"eglGetConfigs",   "(" DISPLAY "[" CONFIG "I[I)Z", (void*)jni_eglGetConfigs },
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 75b07de..f293cba 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -46,22 +46,16 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-Caches::Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO),
-        lastDstMode(GL_ZERO), currentProgram(NULL) {
+Caches::Caches(): Singleton<Caches>(), mInitialized(false) {
     GLint maxTextureUnits;
     glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
     if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
         LOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
     }
 
-    glGenBuffers(1, &meshBuffer);
-    glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
-    glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
-
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
 
-    mCurrentBuffer = meshBuffer;
-    mRegionMesh = NULL;
+    init();
 
     mDebugLevel = readDebugLevel();
     LOGD("Enabling debug mode %d", mDebugLevel);
@@ -71,8 +65,40 @@
 #endif
 }
 
-Caches::~Caches() {
+void Caches::init() {
+    if (mInitialized) return;
+
+    glGenBuffers(1, &meshBuffer);
+    glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
+    glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
+
+    mCurrentBuffer = meshBuffer;
+    mRegionMesh = NULL;
+
+    blend = false;
+    lastSrcMode = GL_ZERO;
+    lastDstMode = GL_ZERO;
+    currentProgram = NULL;
+
+    mInitialized = true;
+}
+
+void Caches::terminate() {
+    if (!mInitialized) return;
+
+    glDeleteBuffers(1, &meshBuffer);
+    mCurrentBuffer = 0;
+
+    glDeleteBuffers(1, &mRegionMeshIndices);
     delete[] mRegionMesh;
+    mRegionMesh = NULL;
+
+    fboCache.clear();
+
+    programCache.clear();
+    currentProgram = NULL;
+
+    mInitialized = false;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 9b0d7c6e..5e58a9e 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -86,7 +86,6 @@
 
 class ANDROID_API Caches: public Singleton<Caches> {
     Caches();
-    ~Caches();
 
     friend class Singleton<Caches>;
 
@@ -109,6 +108,11 @@
     };
 
     /**
+     * Initializes the cache.
+     */
+    void init();
+
+    /**
      * Flush the cache.
      *
      * @param mode Indicates how much of the cache should be flushed
@@ -116,6 +120,12 @@
     void flush(FlushMode mode);
 
     /**
+     * Destroys all resources associated with this cache. This should
+     * be called after a flush(kFlushMode_Full).
+     */
+    void terminate();
+
+    /**
      * Indicates whether the renderer is in debug mode.
      * This debug mode provides limited information to app developers.
      */
@@ -194,6 +204,7 @@
 
 private:
     DebugLevel mDebugLevel;
+    bool mInitialized;
 }; // class Caches
 
 }; // namespace uirenderer
diff --git a/opengl/java/android/opengl/EGLLogWrapper.java b/opengl/java/android/opengl/EGLLogWrapper.java
index 6c0fdb3..36e88a2 100644
--- a/opengl/java/android/opengl/EGLLogWrapper.java
+++ b/opengl/java/android/opengl/EGLLogWrapper.java
@@ -314,6 +314,16 @@
         checkError();
         return result;
     }
+    
+    /** @hide **/
+    public boolean eglReleaseThread() {
+        begin("eglReleaseThread");
+        end();
+        boolean result = mEgl10.eglReleaseThread();
+        returns(result);
+        checkError();
+        return result;
+    }
 
     public boolean eglSwapBuffers(EGLDisplay display, EGLSurface surface) {
         begin("eglInitialize");
diff --git a/opengl/java/com/google/android/gles_jni/EGLImpl.java b/opengl/java/com/google/android/gles_jni/EGLImpl.java
index 51d6ca8..6992019 100644
--- a/opengl/java/com/google/android/gles_jni/EGLImpl.java
+++ b/opengl/java/com/google/android/gles_jni/EGLImpl.java
@@ -31,6 +31,8 @@
     public native boolean     eglInitialize(EGLDisplay display, int[] major_minor);
     public native boolean     eglQueryContext(EGLDisplay display, EGLContext context, int attribute, int[] value);
     public native boolean     eglQuerySurface(EGLDisplay display, EGLSurface surface, int attribute, int[] value);
+    /** @hide **/
+    public native boolean     eglReleaseThread();
     public native boolean     eglChooseConfig(EGLDisplay display, int[] attrib_list, EGLConfig[] configs, int config_size, int[] num_config);
     public native boolean     eglGetConfigAttrib(EGLDisplay display, EGLConfig config, int attribute, int[] value);
     public native boolean     eglGetConfigs(EGLDisplay display, EGLConfig[] configs, int config_size, int[] num_config);
@@ -44,6 +46,9 @@
     public native boolean     eglCopyBuffers(EGLDisplay display, EGLSurface surface, Object native_pixmap);
     public native boolean     eglWaitGL();
     public native boolean     eglWaitNative(int engine, Object bindTarget);
+    
+    /** @hide **/
+    public static native int  getInitCount(EGLDisplay display);
 
     public EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, int[] attrib_list) {
         int eglContextId = _eglCreateContext(display, config, share_context, attrib_list);
@@ -85,7 +90,7 @@
             eglSurfaceId = _eglCreateWindowSurface(display, config, sur, attrib_list);
         } else if (native_window instanceof SurfaceTexture) {
             eglSurfaceId = _eglCreateWindowSurfaceTexture(display, config,
-                    (SurfaceTexture) native_window, attrib_list);
+                    native_window, attrib_list);
         } else {
             throw new java.lang.UnsupportedOperationException(
                 "eglCreateWindowSurface() can only be called with an instance of " +
diff --git a/opengl/java/javax/microedition/khronos/egl/EGL10.java b/opengl/java/javax/microedition/khronos/egl/EGL10.java
index 2ae793a..cf58888 100644
--- a/opengl/java/javax/microedition/khronos/egl/EGL10.java
+++ b/opengl/java/javax/microedition/khronos/egl/EGL10.java
@@ -114,6 +114,8 @@
     boolean     eglQueryContext(EGLDisplay display, EGLContext context, int attribute, int[] value);
     String      eglQueryString(EGLDisplay display, int name);
     boolean     eglQuerySurface(EGLDisplay display, EGLSurface surface, int attribute, int[] value);
+    /** @hide **/
+    boolean     eglReleaseThread();
     boolean     eglSwapBuffers(EGLDisplay display, EGLSurface surface);
     boolean     eglTerminate(EGLDisplay display);
     boolean     eglWaitGL();
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 1c1092c..e0a367d 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -91,6 +91,8 @@
     inline bool isValid() const { return magic == '_dpy'; }
     inline bool isAlive() const { return isValid(); }
 
+    inline uint32_t getRefsCount() const { return refs; }
+
     struct strings_t {
         char const * vendor;
         char const * version;