AI 143890: Fixes #1749387. Improve the pooling of the VelocityTracker class. This introduces a new, hidden, API for pooling objects easily.
  BUG=1749387

Automated import of CL 143890
diff --git a/core/java/android/util/FinitePool.java b/core/java/android/util/FinitePool.java
new file mode 100644
index 0000000..3ef8293
--- /dev/null
+++ b/core/java/android/util/FinitePool.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package android.util;
+
+/**
+ * @hide
+ */
+class FinitePool<T extends Poolable<T>> implements Pool<T> {
+    /**
+     * Factory used to create new pool objects
+     */
+    private final PoolableManager<T> mManager;
+    /**
+     * Maximum number of objects in the pool
+     */
+    private final int mLimit;
+    /**
+     * If true, mLimit is ignored
+     */
+    private final boolean mInfinite;
+
+    /**
+     * Next object to acquire
+     */
+    private T mRoot;
+    /**
+     * Number of objects in the pool
+     */
+    private int mPoolCount;
+
+    FinitePool(PoolableManager<T> manager) {
+        mManager = manager;
+        mLimit = 0;
+        mInfinite = true;
+    }
+
+    FinitePool(PoolableManager<T> manager, int limit) {
+        if (limit <= 0) throw new IllegalArgumentException("The pool limit must be > 0");
+
+        mManager = manager;
+        mLimit = limit;
+        mInfinite = false;
+    }
+
+    public T acquire() {
+        T element;
+
+        if (mRoot != null) {
+            element = mRoot;
+            mRoot = element.getNextPoolable();
+            mPoolCount--;
+        } else {
+            element = mManager.newInstance();
+        }
+
+        if (element != null) {
+            element.setNextPoolable(null);
+            mManager.onAcquired(element);            
+        }
+
+        return element;
+    }
+
+    public void release(T element) {
+        if (mInfinite || mPoolCount < mLimit) {
+            mPoolCount++;
+            element.setNextPoolable(mRoot);
+            mRoot = element;
+        }
+        mManager.onReleased(element);
+    }
+}
diff --git a/core/java/android/util/Pool.java b/core/java/android/util/Pool.java
new file mode 100644
index 0000000..8cd4f3e
--- /dev/null
+++ b/core/java/android/util/Pool.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package android.util;
+
+/**
+ * @hide
+ */
+public interface Pool<T extends Poolable<T>> {
+    public abstract T acquire();
+    public abstract void release(T element);
+}
diff --git a/core/java/android/util/PoolFactory.java b/core/java/android/util/PoolFactory.java
new file mode 100644
index 0000000..4f72bf7
--- /dev/null
+++ b/core/java/android/util/PoolFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package android.util;
+
+/**
+ * @hide
+ */
+public class PoolFactory {
+    private PoolFactory() {
+    }
+
+    public static <T extends Poolable<T>> Pool<T> simplePool(PoolableManager<T> manager) {
+        return new FinitePool<T>(manager);
+    }
+    
+    public static <T extends Poolable<T>> Pool<T> finitePool(PoolableManager<T> manager, int limit) {
+        return new FinitePool<T>(manager, limit);
+    }
+
+    public static <T extends Poolable<T>> Pool<T> synchronizedPool(Pool<T> pool) {
+        return new SynchronizedPool<T>(pool);
+    }
+
+    public static <T extends Poolable<T>> Pool<T> synchronizedPool(Pool<T> pool, Object lock) {
+        return new SynchronizedPool<T>(pool, lock);
+    }
+}
diff --git a/core/java/android/util/Poolable.java b/core/java/android/util/Poolable.java
new file mode 100644
index 0000000..fd9bd9b
--- /dev/null
+++ b/core/java/android/util/Poolable.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package android.util;
+
+/**
+ * @hide
+ */
+public interface Poolable<T> {
+    void setNextPoolable(T element);
+    T getNextPoolable();
+}
diff --git a/core/java/android/util/PoolableManager.java b/core/java/android/util/PoolableManager.java
new file mode 100644
index 0000000..8773e63
--- /dev/null
+++ b/core/java/android/util/PoolableManager.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package android.util;
+
+/**
+ * @hide
+ */
+public interface PoolableManager<T extends Poolable<T>> {
+    T newInstance();
+
+    void onAcquired(T element);
+    void onReleased(T element);
+}
diff --git a/core/java/android/util/SynchronizedPool.java b/core/java/android/util/SynchronizedPool.java
new file mode 100644
index 0000000..651e0c3
--- /dev/null
+++ b/core/java/android/util/SynchronizedPool.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package android.util;
+
+/**
+ *
+ * @hide
+ */
+class SynchronizedPool<T extends Poolable<T>> implements Pool<T> {
+    private final Pool<T> mPool;
+    private final Object mLock;
+
+    public SynchronizedPool(Pool<T> pool) {
+        mPool = pool;
+        mLock = this;
+    }
+
+    public SynchronizedPool(Pool<T> pool, Object lock) {
+        mPool = pool;
+        mLock = lock;
+    }
+
+    public T acquire() {
+        synchronized (mLock) {
+            return mPool.acquire();
+        }
+    }
+
+    public void release(T element) {
+        synchronized (mLock) {
+            mPool.release(element);
+        }
+    }
+}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 3951b2c3..256525a 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -18,6 +18,10 @@
 
 import android.util.Config;
 import android.util.Log;
+import android.util.Poolable;
+import android.util.Pool;
+import android.util.PoolFactory;
+import android.util.PoolableManager;
 
 /**
  * Helper for tracking the velocity of touch events, for implementing
@@ -28,53 +32,72 @@
  * {@link #computeCurrentVelocity(int)} and then {@link #getXVelocity()}
  * and {@link #getXVelocity()}.
  */
-public final class VelocityTracker {
+public final class VelocityTracker implements Poolable<VelocityTracker> {
     static final String TAG = "VelocityTracker";
     static final boolean DEBUG = false;
     static final boolean localLOGV = DEBUG || Config.LOGV;
-    
+
     static final int NUM_PAST = 10;
     static final int LONGEST_PAST_TIME = 200;
-    
+
     static final VelocityTracker[] mPool = new VelocityTracker[1];
-    
+    private static final Pool<VelocityTracker> sPool = PoolFactory.synchronizedPool(
+            PoolFactory.finitePool(new PoolableManager<VelocityTracker>() {
+                public VelocityTracker newInstance() {
+                    return new VelocityTracker();
+                }
+
+                public void onAcquired(VelocityTracker element) {
+                    element.clear();
+                }
+
+                public void onReleased(VelocityTracker element) {
+                }
+            }, 2));
+
     final float mPastX[] = new float[NUM_PAST];
     final float mPastY[] = new float[NUM_PAST];
     final long mPastTime[] = new long[NUM_PAST];
-   
+
     float mYVelocity;
     float mXVelocity;
-    
+
+    private VelocityTracker mNext;
+
     /**
      * Retrieve a new VelocityTracker object to watch the velocity of a
      * motion.  Be sure to call {@link #recycle} when done.  You should
      * generally only maintain an active object while tracking a movement,
      * so that the VelocityTracker can be re-used elsewhere.
-     * 
+     *
      * @return Returns a new VelocityTracker.
      */
     static public VelocityTracker obtain() {
-        synchronized (mPool) {
-            VelocityTracker vt = mPool[0];
-            if (vt != null) {
-                vt.clear();
-                mPool[0] = null;
-                return vt;
-            }
-            return new VelocityTracker();
-        }
+        return sPool.acquire();
     }
-    
+
     /**
      * Return a VelocityTracker object back to be re-used by others.  You must
      * not touch the object after calling this function.
      */
     public void recycle() {
-        synchronized (mPool) {
-            mPool[0] = this;
-        }
+        sPool.release(this);
     }
-    
+
+    /**
+     * @hide
+     */
+    public void setNextPoolable(VelocityTracker element) {
+        mNext = element;
+    }
+
+    /**
+     * @hide
+     */
+    public VelocityTracker getNextPoolable() {
+        return mNext;
+    }
+
     private VelocityTracker() {
     }
     
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 04447ca..9f4143c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -45,6 +45,10 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.Poolable;
+import android.util.Pool;
+import android.util.PoolFactory;
+import android.util.PoolableManager;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.animation.Animation;
 import android.view.inputmethod.InputConnection;
@@ -7827,26 +7831,24 @@
          * For performance purposes, this class also implements a pool of up to
          * POOL_LIMIT objects that get reused. This reduces memory allocations
          * whenever possible.
-         *
-         * The pool is implemented as a linked list of InvalidateInfo object with
-         * the root pointing to the next available InvalidateInfo. If the root
-         * is null (i.e. when all instances from the pool have been acquired),
-         * then a new InvalidateInfo is created and returned to the caller.
-         *
-         * An InvalidateInfo is sent back to the pool by calling its release()
-         * method. If the pool is full the object is simply discarded.
-         *
-         * This implementation follows the object pool pattern used in the
-         * MotionEvent class.
          */
-        static class InvalidateInfo {
+        static class InvalidateInfo implements Poolable<InvalidateInfo> {
             private static final int POOL_LIMIT = 10;
-            private static final Object sLock = new Object();
+            private static final Pool<InvalidateInfo> sPool = PoolFactory.synchronizedPool(
+                    PoolFactory.finitePool(new PoolableManager<InvalidateInfo>() {
+                        public InvalidateInfo newInstance() {
+                            return new InvalidateInfo();
+                        }
 
-            private static int sAcquiredCount = 0;
-            private static InvalidateInfo sRoot;
+                        public void onAcquired(InvalidateInfo element) {
+                        }
 
-            private InvalidateInfo next;
+                        public void onReleased(InvalidateInfo element) {
+                        }
+                    }, POOL_LIMIT)
+            );
+
+            private InvalidateInfo mNext;
 
             View target;
 
@@ -7855,28 +7857,20 @@
             int right;
             int bottom;
 
+            public void setNextPoolable(InvalidateInfo element) {
+                mNext = element;
+            }
+
+            public InvalidateInfo getNextPoolable() {
+                return mNext;
+            }
+
             static InvalidateInfo acquire() {
-                synchronized (sLock) {
-                    if (sRoot == null) {
-                        return new InvalidateInfo();
-                    }
-
-                    InvalidateInfo info = sRoot;
-                    sRoot = info.next;
-                    sAcquiredCount--;
-
-                    return info;
-                }
+                return sPool.acquire();
             }
 
             void release() {
-                synchronized (sLock) {
-                    if (sAcquiredCount < POOL_LIMIT) {
-                        sAcquiredCount++;
-                        next = sRoot;
-                        sRoot = this;
-                    }
-                }
+                sPool.release(this);
             }
         }