Destroy the hardware renderer when ViewRootImpl's die is post-poned
Bug #6109035

ViewRootImpl.die() can be invoked in such a way that doDie() will be
executed later. On memory limited device, an eglTerminate() may happen
before doDie() is executed which leads to unstable behaviors. This
change makes sure the renderer is destroyed as soon as possible.

Change-Id: I3322410cdd744b464951e2055aeade6069d1d673
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2a3e213..deb711d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -404,6 +404,7 @@
                     try {
                         fd.close();
                     } catch (IOException e) {
+                        // Ignore
                     }
                 }
                 return;
@@ -412,6 +413,7 @@
                 try {
                     profileFd.close();
                 } catch (IOException e) {
+                    // Ignore
                 }
             }
             profileFile = file;
@@ -843,14 +845,13 @@
             FileOutputStream fout = new FileOutputStream(fd);
             PrintWriter pw = new PrintWriter(fout);
             try {
-                return dumpMemInfo(pw, checkin, all, args);
+                return dumpMemInfo(pw, checkin, all);
             } finally {
                 pw.flush();
             }
         }
 
-        private Debug.MemoryInfo dumpMemInfo(PrintWriter pw, boolean checkin, boolean all,
-                String[] args) {
+        private Debug.MemoryInfo dumpMemInfo(PrintWriter pw, boolean checkin, boolean all) {
             long nativeMax = Debug.getNativeHeapSize() / 1024;
             long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
             long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
@@ -1458,17 +1459,6 @@
         return dm;
     }
 
-    static Configuration applyConfigCompat(Configuration config, CompatibilityInfo compat) {
-        if (config == null) {
-            return null;
-        }
-        if (compat != null && !compat.supportsScreen()) {
-            config = new Configuration(config);
-            compat.applyToConfiguration(config);
-        }
-        return config;
-    }
-
     private Configuration mMainThreadConfig = new Configuration();
     Configuration applyConfigCompatMainThread(Configuration config, CompatibilityInfo compat) {
         if (config == null) {
@@ -2509,7 +2499,7 @@
         return r;
     }
 
-    final void cleanUpPendingRemoveWindows(ActivityClientRecord r) {
+    static final void cleanUpPendingRemoveWindows(ActivityClientRecord r) {
         if (r.mPendingRemoveWindow != null) {
             r.mPendingRemoveWindowManager.removeViewImmediate(r.mPendingRemoveWindow);
             IBinder wtoken = r.mPendingRemoveWindow.getWindowToken();
@@ -3437,15 +3427,12 @@
                 = new ArrayList<ComponentCallbacks2>();
 
         if (mActivities.size() > 0) {
-            Iterator<ActivityClientRecord> it = mActivities.values().iterator();
-            while (it.hasNext()) {
-                ActivityClientRecord ar = it.next();
+            for (ActivityClientRecord ar : mActivities.values()) {
                 Activity a = ar.activity;
                 if (a != null) {
                     Configuration thisConfig = applyConfigCompatMainThread(newConfig,
                             ar.packageInfo.mCompatibilityInfo.getIfNeeded());
-                    if (!ar.activity.mFinished && (allActivities ||
-                            (a != null && !ar.paused))) {
+                    if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
                         // If the activity is currently resumed, its configuration
                         // needs to change right now.
                         callbacks.add(a);
@@ -3455,24 +3442,24 @@
                         // the activity manager may, before then, decide the
                         // activity needs to be destroyed to handle its new
                         // configuration.
-                        if (DEBUG_CONFIGURATION) Slog.v(TAG, "Setting activity "
-                                + ar.activityInfo.name + " newConfig=" + thisConfig);
+                        if (DEBUG_CONFIGURATION) {
+                            Slog.v(TAG, "Setting activity "
+                                    + ar.activityInfo.name + " newConfig=" + thisConfig);
+                        }
                         ar.newConfig = thisConfig;
                     }
                 }
             }
         }
         if (mServices.size() > 0) {
-            Iterator<Service> it = mServices.values().iterator();
-            while (it.hasNext()) {
-                callbacks.add(it.next());
+            for (Service service : mServices.values()) {
+                callbacks.add(service);
             }
         }
         synchronized (mProviderMap) {
             if (mLocalProviders.size() > 0) {
-                Iterator<ProviderClientRecord> it = mLocalProviders.values().iterator();
-                while (it.hasNext()) {
-                    callbacks.add(it.next().mLocalProvider);
+                for (ProviderClientRecord providerClientRecord : mLocalProviders.values()) {
+                    callbacks.add(providerClientRecord.mLocalProvider);
                 }
             }
         }
@@ -3484,8 +3471,7 @@
         return callbacks;
     }
 
-    private final void performConfigurationChanged(
-            ComponentCallbacks2 cb, Configuration config) {
+    private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) {
         // Only for Activity objects, check that they actually call up to their
         // superclass implementation.  ComponentCallbacks2 is an interface, so
         // we check the runtime type and act accordingly.
@@ -3692,7 +3678,7 @@
         }
     }
 
-    final void handleDumpHeap(boolean managed, DumpHeapData dhd) {
+    static final void handleDumpHeap(boolean managed, DumpHeapData dhd) {
         if (managed) {
             try {
                 Debug.dumpHprofData(dhd.path, dhd.fd.getFileDescriptor());
@@ -3769,7 +3755,7 @@
         }
 
         final int N = callbacks.size();
-        for (int i=0; i<N; i++) {
+        for (int i = 0; i < N; i++) {
             callbacks.get(i).onTrimMemory(level);
         }
         WindowManagerImpl.getDefault().terminateEgl();
@@ -4057,9 +4043,7 @@
         final ArrayList<IActivityManager.ContentProviderHolder> results =
             new ArrayList<IActivityManager.ContentProviderHolder>();
 
-        Iterator<ProviderInfo> i = providers.iterator();
-        while (i.hasNext()) {
-            ProviderInfo cpi = i.next();
+        for (ProviderInfo cpi : providers) {
             StringBuilder buf = new StringBuilder(128);
             buf.append("Pub ");
             buf.append(cpi.authority);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 1f140e95..f98cfc0 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -1121,6 +1121,7 @@
                         GLES20Canvas.terminateCaches();
 
                         sEgl.eglDestroyContext(sEglDisplay, eglContext);
+                        sEglContextStorage.set(null);
                         sEglContextStorage.remove();
 
                         sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
@@ -1134,7 +1135,6 @@
                         sEglDisplay = null;
                         sEglConfig = null;
                         sPbuffer = null;
-                        sEglContextStorage.set(null);
                     }
                 }
             }
@@ -1238,7 +1238,7 @@
         }
 
         private static void destroyHardwareLayer(View view) {
-            view.destroyLayer();
+            view.destroyLayer(true);
 
             if (view instanceof ViewGroup) {
                 ViewGroup group = (ViewGroup) view;
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index fc02cc1..83999a1 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -299,7 +299,7 @@
     }
 
     @Override
-    boolean destroyLayer() {
+    boolean destroyLayer(boolean valid) {
         return false;
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ffffc73..63cf4ae 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10018,7 +10018,7 @@
 
         destroyDrawingCache();
 
-        destroyLayer();
+        destroyLayer(false);
 
         if (mAttachInfo != null) {
             if (mDisplayList != null) {
@@ -10394,7 +10394,7 @@
         // Destroy any previous software drawing cache if needed
         switch (mLayerType) {
             case LAYER_TYPE_HARDWARE:
-                destroyLayer();
+                destroyLayer(false);
                 // fall through - non-accelerated views may use software layer mechanism instead
             case LAYER_TYPE_SOFTWARE:
                 destroyDrawingCache();
@@ -10532,11 +10532,12 @@
      * @see #setLayerType(int, android.graphics.Paint) 
      * @see #LAYER_TYPE_HARDWARE
      */
-    boolean destroyLayer() {
+    boolean destroyLayer(boolean valid) {
         if (mHardwareLayer != null) {
             AttachInfo info = mAttachInfo;
             if (info != null && info.mHardwareRenderer != null &&
-                    info.mHardwareRenderer.isEnabled() && info.mHardwareRenderer.validate()) {
+                    info.mHardwareRenderer.isEnabled() &&
+                    (valid || info.mHardwareRenderer.validate())) {
                 mHardwareLayer.destroy();
                 mHardwareLayer = null;
 
@@ -10560,7 +10561,7 @@
      * @hide
      */
     protected void destroyHardwareResources() {
-        destroyLayer();
+        destroyLayer(true);
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b8fbf17..6ccac78 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1820,7 +1820,7 @@
      * Resets the cancel next up flag.
      * Returns true if the flag was previously set.
      */
-    private boolean resetCancelNextUpFlag(View view) {
+    private static boolean resetCancelNextUpFlag(View view) {
         if ((view.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
             view.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
             return true;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 14b8084..befc1c6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1809,7 +1809,7 @@
      *
      * @return The measure spec to use to measure the root view.
      */
-    private int getRootMeasureSpec(int windowSize, int rootDimension) {
+    private static int getRootMeasureSpec(int windowSize, int rootDimension) {
         int measureSpec;
         switch (rootDimension) {
 
@@ -2432,12 +2432,12 @@
                 mAccessibilityInteractionConnectionManager);
         removeSendWindowContentChangedCallback();
 
+        destroyHardwareRenderer();
+
         mView = null;
         mAttachInfo.mRootView = null;
         mAttachInfo.mSurface = null;
 
-        destroyHardwareRenderer();
-
         mSurface.release();
 
         if (mInputQueueCallback != null && mInputQueue != null) {
@@ -2893,7 +2893,7 @@
      * @param focused The currently focused view.
      * @return An appropriate view, or null if no such view exists.
      */
-    private ViewGroup findAncestorToTakeFocusInTouchMode(View focused) {
+    private static ViewGroup findAncestorToTakeFocusInTouchMode(View focused) {
         ViewParent parent = focused.getParent();
         while (parent instanceof ViewGroup) {
             final ViewGroup vgParent = (ViewGroup) parent;
@@ -3763,7 +3763,7 @@
         }
     }
 
-    private void getGfxInfo(View view, int[] info) {
+    private static void getGfxInfo(View view, int[] info) {
         DisplayList displayList = view.mDisplayList;
         info[0]++;
         if (displayList != null) {
@@ -3784,6 +3784,7 @@
         if (immediate) {
             doDie();
         } else {
+            destroyHardwareRenderer();
             mHandler.sendEmptyMessage(MSG_DIE);
         }
     }
@@ -3826,10 +3827,18 @@
     }
 
     private void destroyHardwareRenderer() {
-        if (mAttachInfo.mHardwareRenderer != null) {
-            mAttachInfo.mHardwareRenderer.destroy(true);
-            mAttachInfo.mHardwareRenderer = null;
-            mAttachInfo.mHardwareAccelerated = false;
+        AttachInfo attachInfo = mAttachInfo;
+        HardwareRenderer hardwareRenderer = attachInfo.mHardwareRenderer;
+
+        if (hardwareRenderer != null) {
+            if (mView != null) {
+                hardwareRenderer.destroyHardwareResources(mView);
+            }
+            hardwareRenderer.destroy(true);
+            hardwareRenderer.setRequested(false);
+
+            attachInfo.mHardwareRenderer = null;
+            attachInfo.mHardwareAccelerated = false;
         }
     }
 
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 0e4a30f..f2ee9f9 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -354,7 +354,7 @@
     View removeViewLocked(int index) {
         ViewRootImpl root = mRoots[index];
         View view = root.getView();
-        
+
         // Don't really remove until we have matched all calls to add().
         root.mAddNesting--;
         if (root.mAddNesting > 0) {