Revert "Remove ViewTreeObserver allocations"

This reverts commit b999cc118fe430699e9a67d5dab355125b873abb.

There was an assumption in this earlier change that observer dispatching could not be
recursive - we could only ever have one iteration on the observer listener list. This
assumption broke down in a specific app, and maybe in more, so reverting the change for now.
We should probably find a way to accomplish the same allocation-minimizing goal without
causing exceptions when violating our assumptions.

Issue #6620795 [Application compatibility] Lufthansa app crashes

Change-Id: I1c1f9ad329c14398feb0e74ce77e1a07111f7d1f
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 6a8a60a..1c5d436 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -20,6 +20,7 @@
 import android.graphics.Region;
 
 import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * A view tree observer is used to register listeners that can be notified of global
@@ -31,12 +32,12 @@
  * for more information.
  */
 public final class ViewTreeObserver {
-    private CopyOnWriteArray<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
-    private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
-    private CopyOnWriteArray<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
-    private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
-    private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners;
-    private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners;
+    private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
+    private CopyOnWriteArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
+    private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
+    private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
+    private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners;
+    private ArrayList<OnPreDrawListener> mOnPreDrawListeners;
     private ArrayList<OnDrawListener> mOnDrawListeners;
 
     private boolean mAlive = true;
@@ -146,7 +147,7 @@
          * windows behind it should be placed.
          */
         public final Rect contentInsets = new Rect();
-
+        
         /**
          * Offsets from the frame of the window at which windows behind it
          * are visible.
@@ -165,13 +166,13 @@
          * can be touched.
          */
         public static final int TOUCHABLE_INSETS_FRAME = 0;
-
+        
         /**
          * Option for {@link #setTouchableInsets(int)}: the area inside of
          * the content insets can be touched.
          */
         public static final int TOUCHABLE_INSETS_CONTENT = 1;
-
+        
         /**
          * Option for {@link #setTouchableInsets(int)}: the area inside of
          * the visible insets can be touched.
@@ -194,7 +195,7 @@
         }
 
         int mTouchableInsets;
-
+        
         void reset() {
             contentInsets.setEmpty();
             visibleInsets.setEmpty();
@@ -230,7 +231,7 @@
             mTouchableInsets = other.mTouchableInsets;
         }
     }
-
+    
     /**
      * Interface definition for a callback to be invoked when layout has
      * completed and the client can compute its interior insets.
@@ -327,7 +328,7 @@
         checkIsAlive();
 
         if (mOnGlobalFocusListeners == null) {
-            mOnGlobalFocusListeners = new CopyOnWriteArray<OnGlobalFocusChangeListener>();
+            mOnGlobalFocusListeners = new CopyOnWriteArrayList<OnGlobalFocusChangeListener>();
         }
 
         mOnGlobalFocusListeners.add(listener);
@@ -362,7 +363,7 @@
         checkIsAlive();
 
         if (mOnGlobalLayoutListeners == null) {
-            mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>();
+            mOnGlobalLayoutListeners = new CopyOnWriteArrayList<OnGlobalLayoutListener>();
         }
 
         mOnGlobalLayoutListeners.add(listener);
@@ -412,7 +413,7 @@
         checkIsAlive();
 
         if (mOnPreDrawListeners == null) {
-            mOnPreDrawListeners = new CopyOnWriteArray<OnPreDrawListener>();
+            mOnPreDrawListeners = new ArrayList<OnPreDrawListener>();
         }
 
         mOnPreDrawListeners.add(listener);
@@ -484,7 +485,7 @@
         checkIsAlive();
 
         if (mOnScrollChangedListeners == null) {
-            mOnScrollChangedListeners = new CopyOnWriteArray<OnScrollChangedListener>();
+            mOnScrollChangedListeners = new CopyOnWriteArrayList<OnScrollChangedListener>();
         }
 
         mOnScrollChangedListeners.add(listener);
@@ -518,7 +519,7 @@
         checkIsAlive();
 
         if (mOnTouchModeChangeListeners == null) {
-            mOnTouchModeChangeListeners = new CopyOnWriteArray<OnTouchModeChangeListener>();
+            mOnTouchModeChangeListeners = new CopyOnWriteArrayList<OnTouchModeChangeListener>();
         }
 
         mOnTouchModeChangeListeners.add(listener);
@@ -557,7 +558,7 @@
 
         if (mOnComputeInternalInsetsListeners == null) {
             mOnComputeInternalInsetsListeners =
-                    new CopyOnWriteArray<OnComputeInternalInsetsListener>();
+                    new CopyOnWriteArrayList<OnComputeInternalInsetsListener>();
         }
 
         mOnComputeInternalInsetsListeners.add(listener);
@@ -621,16 +622,10 @@
         // perform the dispatching. The iterator is a safe guard against listeners that
         // could mutate the list by calling the various add/remove methods. This prevents
         // the array from being modified while we iterate it.
-        final CopyOnWriteArray<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
+        final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
         if (listeners != null && listeners.size() > 0) {
-            CopyOnWriteArray.Access<OnGlobalFocusChangeListener> access = listeners.start();
-            try {
-                int count = access.size();
-                for (int i = 0; i < count; i++) {
-                    access.get(i).onGlobalFocusChanged(oldFocus, newFocus);
-                }
-            } finally {
-                listeners.end();
+            for (OnGlobalFocusChangeListener listener : listeners) {
+                listener.onGlobalFocusChanged(oldFocus, newFocus);
             }
         }
     }
@@ -645,16 +640,10 @@
         // perform the dispatching. The iterator is a safe guard against listeners that
         // could mutate the list by calling the various add/remove methods. This prevents
         // the array from being modified while we iterate it.
-        final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
+        final CopyOnWriteArrayList<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
         if (listeners != null && listeners.size() > 0) {
-            CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start();
-            try {
-                int count = access.size();
-                for (int i = 0; i < count; i++) {
-                    access.get(i).onGlobalLayout();
-                }
-            } finally {
-                listeners.end();
+            for (OnGlobalLayoutListener listener : listeners) {
+                listener.onGlobalLayout();
             }
         }
     }
@@ -669,17 +658,17 @@
      */
     @SuppressWarnings("unchecked")
     public final boolean dispatchOnPreDraw() {
+        // NOTE: we *must* clone the listener list to perform the dispatching.
+        // The clone is a safe guard against listeners that
+        // could mutate the list by calling the various add/remove methods. This prevents
+        // the array from being modified while we process it.
         boolean cancelDraw = false;
-        final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;
-        if (listeners != null && listeners.size() > 0) {
-            CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start();
-            try {
-                int count = access.size();
-                for (int i = 0; i < count; i++) {
-                    cancelDraw |= !(access.get(i).onPreDraw());
-                }
-            } finally {
-                listeners.end();
+        if (mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0) {
+            final ArrayList<OnPreDrawListener> listeners =
+                    (ArrayList<OnPreDrawListener>) mOnPreDrawListeners.clone();
+            int numListeners = listeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                cancelDraw |= !(listeners.get(i).onPreDraw());
             }
         }
         return cancelDraw;
@@ -704,17 +693,11 @@
      * @param inTouchMode True if the touch mode is now enabled, false otherwise.
      */
     final void dispatchOnTouchModeChanged(boolean inTouchMode) {
-        final CopyOnWriteArray<OnTouchModeChangeListener> listeners =
+        final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners =
                 mOnTouchModeChangeListeners;
         if (listeners != null && listeners.size() > 0) {
-            CopyOnWriteArray.Access<OnTouchModeChangeListener> access = listeners.start();
-            try {
-                int count = access.size();
-                for (int i = 0; i < count; i++) {
-                    access.get(i).onTouchModeChanged(inTouchMode);
-                }
-            } finally {
-                listeners.end();
+            for (OnTouchModeChangeListener listener : listeners) {
+                listener.onTouchModeChanged(inTouchMode);
             }
         }
     }
@@ -727,16 +710,10 @@
         // perform the dispatching. The iterator is a safe guard against listeners that
         // could mutate the list by calling the various add/remove methods. This prevents
         // the array from being modified while we iterate it.
-        final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
+        final CopyOnWriteArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
         if (listeners != null && listeners.size() > 0) {
-            CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start();
-            try {
-                int count = access.size();
-                for (int i = 0; i < count; i++) {
-                    access.get(i).onScrollChanged();
-                }
-            } finally {
-                listeners.end();
+            for (OnScrollChangedListener listener : listeners) {
+                listener.onScrollChanged();
             }
         }
     }
@@ -745,11 +722,11 @@
      * Returns whether there are listeners for computing internal insets.
      */
     final boolean hasComputeInternalInsetsListeners() {
-        final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
+        final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners =
                 mOnComputeInternalInsetsListeners;
         return (listeners != null && listeners.size() > 0);
     }
-
+    
     /**
      * Calls all listeners to compute the current insets.
      */
@@ -758,105 +735,12 @@
         // perform the dispatching. The iterator is a safe guard against listeners that
         // could mutate the list by calling the various add/remove methods. This prevents
         // the array from being modified while we iterate it.
-        final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
+        final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners =
                 mOnComputeInternalInsetsListeners;
         if (listeners != null && listeners.size() > 0) {
-            CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start();
-            try {
-                int count = access.size();
-                for (int i = 0; i < count; i++) {
-                    access.get(i).onComputeInternalInsets(inoutInfo);
-                }
-            } finally {
-                listeners.end();
+            for (OnComputeInternalInsetsListener listener : listeners) {
+                listener.onComputeInternalInsets(inoutInfo);
             }
         }
     }
-
-    /**
-     * Copy on write array. This array is not thread safe, and only one loop can
-     * iterate over this array at any given time. This class avoids allocations
-     * until a concurrent modification happens.
-     * 
-     * Usage:
-     * 
-     * CopyOnWriteArray.Access<MyData> access = array.start();
-     * try {
-     *     for (int i = 0; i < access.size(); i++) {
-     *         MyData d = access.get(i);
-     *     }
-     * } finally {
-     *     access.end();
-     * }
-     */
-    static class CopyOnWriteArray<T> {
-        private ArrayList<T> mData = new ArrayList<T>();
-        private ArrayList<T> mDataCopy;
-
-        private final Access<T> mAccess = new Access<T>();
-
-        private boolean mStart;
-
-        static class Access<T> {
-            private ArrayList<T> mData;
-            private int mSize;
-
-            T get(int index) {
-                return mData.get(index);
-            }
-
-            int size() {
-                return mSize;
-            }
-        }
-
-        CopyOnWriteArray() {
-        }
-
-        private ArrayList<T> getArray() {
-            if (mStart) {
-                if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData);
-                return mDataCopy;
-            }
-            return mData;
-        }
-
-        Access<T> start() {
-            if (mStart) throw new IllegalStateException("Iteration already started");
-            mStart = true;
-            mDataCopy = null;
-            mAccess.mData = mData;
-            mAccess.mSize = mData.size();
-            return mAccess;
-        }
-
-        void end() {
-            if (!mStart) throw new IllegalStateException("Iteration not started");
-            mStart = false;
-            if (mDataCopy != null) {
-                mData = mDataCopy;
-            }
-            mDataCopy = null;
-        }
-
-        int size() {
-            return getArray().size();
-        }
-
-        void add(T item) {
-            getArray().add(item);
-        }
-
-        void addAll(CopyOnWriteArray<T> array) {
-            getArray().addAll(array.mData);
-        }
-
-        void remove(T item) {
-            getArray().remove(item);
-        }
-
-        void clear() {
-            getArray().clear();
-        }
-    }
 }