Some optimizations.

- Don't try to create a thumbnail bitmap on the client side.  This
  wastes 64k, and isn't needed since we are doing screenshots.
- Optimize View to put all of the callback pointers out of line.
  Added a couple new APIs so these don't need to be protected/public.
- Lazily create ViewGroup's cache paint.
- Change FrameworkPerf app to not use HW accel drawing, to give better
  comparison with GB.

Change-Id: Iec56d02459820d74a4cc9c7ec9c1856563c82c7b
diff --git a/api/current.txt b/api/current.txt
index fe0699c..9d38566 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22849,6 +22849,7 @@
     method public void buildDrawingCache();
     method public void buildDrawingCache(boolean);
     method public void buildLayer();
+    method public boolean callOnClick();
     method public boolean canScrollHorizontally(int);
     method public boolean canScrollVertically(int);
     method public void cancelLongPress();
@@ -23000,6 +23001,7 @@
     method public float getY();
     method public boolean hasFocus();
     method public boolean hasFocusable();
+    method public boolean hasOnClickListeners();
     method public boolean hasWindowFocus();
     method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
     method protected void initializeFadingEdge(android.content.res.TypedArray);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8afe9bf..00fe953 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2769,7 +2769,9 @@
             if (info != null) {
                 try {
                     // First create a thumbnail for the activity...
-                    info.thumbnail = createThumbnailBitmap(r);
+                    // For now, don't create the thumbnail here; we are
+                    // doing that by doing a screen snapshot.
+                    info.thumbnail = null; //createThumbnailBitmap(r);
                     info.description = r.activity.onCreateDescription();
                 } catch (Exception e) {
                     if (!mInstrumentation.onException(r.activity, e)) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index fea79d5..70681ac 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1548,7 +1548,7 @@
     int mID = NO_ID;
 
     /**
-     * The stable ID of this view for accessibility porposes.
+     * The stable ID of this view for accessibility purposes.
      */
     int mAccessibilityViewId = NO_ID;
 
@@ -2317,55 +2317,59 @@
     private int mBackgroundResource;
     private boolean mBackgroundSizeChanged;
 
-    /**
-     * Listener used to dispatch focus change events.
-     * This field should be made private, so it is hidden from the SDK.
-     * {@hide}
-     */
-    protected OnFocusChangeListener mOnFocusChangeListener;
+    static class ListenerInfo {
+        /**
+         * Listener used to dispatch focus change events.
+         * This field should be made private, so it is hidden from the SDK.
+         * {@hide}
+         */
+        protected OnFocusChangeListener mOnFocusChangeListener;
 
-    /**
-     * Listeners for layout change events.
-     */
-    private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;
+        /**
+         * Listeners for layout change events.
+         */
+        private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;
 
-    /**
-     * Listeners for attach events.
-     */
-    private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;
+        /**
+         * Listeners for attach events.
+         */
+        private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;
 
-    /**
-     * Listener used to dispatch click events.
-     * This field should be made private, so it is hidden from the SDK.
-     * {@hide}
-     */
-    protected OnClickListener mOnClickListener;
+        /**
+         * Listener used to dispatch click events.
+         * This field should be made private, so it is hidden from the SDK.
+         * {@hide}
+         */
+        public OnClickListener mOnClickListener;
 
-    /**
-     * Listener used to dispatch long click events.
-     * This field should be made private, so it is hidden from the SDK.
-     * {@hide}
-     */
-    protected OnLongClickListener mOnLongClickListener;
+        /**
+         * Listener used to dispatch long click events.
+         * This field should be made private, so it is hidden from the SDK.
+         * {@hide}
+         */
+        protected OnLongClickListener mOnLongClickListener;
 
-    /**
-     * Listener used to build the context menu.
-     * This field should be made private, so it is hidden from the SDK.
-     * {@hide}
-     */
-    protected OnCreateContextMenuListener mOnCreateContextMenuListener;
+        /**
+         * Listener used to build the context menu.
+         * This field should be made private, so it is hidden from the SDK.
+         * {@hide}
+         */
+        protected OnCreateContextMenuListener mOnCreateContextMenuListener;
 
-    private OnKeyListener mOnKeyListener;
+        private OnKeyListener mOnKeyListener;
 
-    private OnTouchListener mOnTouchListener;
+        private OnTouchListener mOnTouchListener;
 
-    private OnHoverListener mOnHoverListener;
+        private OnHoverListener mOnHoverListener;
 
-    private OnGenericMotionListener mOnGenericMotionListener;
+        private OnGenericMotionListener mOnGenericMotionListener;
 
-    private OnDragListener mOnDragListener;
+        private OnDragListener mOnDragListener;
 
-    private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;
+        private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;
+    }
+
+    ListenerInfo mListenerInfo;
 
     /**
      * The application environment this view lives in.
@@ -3346,13 +3350,21 @@
         return mVerticalScrollbarPosition;
     }
 
+    ListenerInfo getListenerInfo() {
+        if (mListenerInfo != null) {
+            return mListenerInfo;
+        }
+        mListenerInfo = new ListenerInfo();
+        return mListenerInfo;
+    }
+
     /**
      * Register a callback to be invoked when focus of this view changed.
      *
      * @param l The callback that will run.
      */
     public void setOnFocusChangeListener(OnFocusChangeListener l) {
-        mOnFocusChangeListener = l;
+        getListenerInfo().mOnFocusChangeListener = l;
     }
 
     /**
@@ -3362,11 +3374,12 @@
      * @param listener The listener that will be called when layout bounds change.
      */
     public void addOnLayoutChangeListener(OnLayoutChangeListener listener) {
-        if (mOnLayoutChangeListeners == null) {
-            mOnLayoutChangeListeners = new ArrayList<OnLayoutChangeListener>();
+        ListenerInfo li = getListenerInfo();
+        if (li.mOnLayoutChangeListeners == null) {
+            li.mOnLayoutChangeListeners = new ArrayList<OnLayoutChangeListener>();
         }
-        if (!mOnLayoutChangeListeners.contains(listener)) {
-            mOnLayoutChangeListeners.add(listener);
+        if (!li.mOnLayoutChangeListeners.contains(listener)) {
+            li.mOnLayoutChangeListeners.add(listener);
         }
     }
 
@@ -3376,10 +3389,11 @@
      * @param listener The listener for layout bounds change.
      */
     public void removeOnLayoutChangeListener(OnLayoutChangeListener listener) {
-        if (mOnLayoutChangeListeners == null) {
+        ListenerInfo li = mListenerInfo;
+        if (li == null || li.mOnLayoutChangeListeners == null) {
             return;
         }
-        mOnLayoutChangeListeners.remove(listener);
+        li.mOnLayoutChangeListeners.remove(listener);
     }
 
     /**
@@ -3393,10 +3407,12 @@
      * @see #removeOnAttachStateChangeListener(OnAttachStateChangeListener)
      */
     public void addOnAttachStateChangeListener(OnAttachStateChangeListener listener) {
-        if (mOnAttachStateChangeListeners == null) {
-            mOnAttachStateChangeListeners = new CopyOnWriteArrayList<OnAttachStateChangeListener>();
+        ListenerInfo li = getListenerInfo();
+        if (li.mOnAttachStateChangeListeners == null) {
+            li.mOnAttachStateChangeListeners
+                    = new CopyOnWriteArrayList<OnAttachStateChangeListener>();
         }
-        mOnAttachStateChangeListeners.add(listener);
+        li.mOnAttachStateChangeListeners.add(listener);
     }
 
     /**
@@ -3407,10 +3423,11 @@
      * @see #addOnAttachStateChangeListener(OnAttachStateChangeListener)
      */
     public void removeOnAttachStateChangeListener(OnAttachStateChangeListener listener) {
-        if (mOnAttachStateChangeListeners == null) {
+        ListenerInfo li = mListenerInfo;
+        if (li == null || li.mOnAttachStateChangeListeners == null) {
             return;
         }
-        mOnAttachStateChangeListeners.remove(listener);
+        li.mOnAttachStateChangeListeners.remove(listener);
     }
 
     /**
@@ -3419,7 +3436,8 @@
      * @return The callback, or null if one is not registered.
      */
     public OnFocusChangeListener getOnFocusChangeListener() {
-        return mOnFocusChangeListener;
+        ListenerInfo li = mListenerInfo;
+        return li != null ? li.mOnFocusChangeListener : null;
     }
 
     /**
@@ -3434,7 +3452,16 @@
         if (!isClickable()) {
             setClickable(true);
         }
-        mOnClickListener = l;
+        getListenerInfo().mOnClickListener = l;
+    }
+
+    /**
+     * Return whether this view has an attached OnClickListener.  Returns
+     * true if there is a listener, false if there is none.
+     */
+    public boolean hasOnClickListeners() {
+        ListenerInfo li = mListenerInfo;
+        return (li != null && li.mOnClickListener != null);
     }
 
     /**
@@ -3449,7 +3476,7 @@
         if (!isLongClickable()) {
             setLongClickable(true);
         }
-        mOnLongClickListener = l;
+        getListenerInfo().mOnLongClickListener = l;
     }
 
     /**
@@ -3463,11 +3490,13 @@
         if (!isLongClickable()) {
             setLongClickable(true);
         }
-        mOnCreateContextMenuListener = l;
+        getListenerInfo().mOnCreateContextMenuListener = l;
     }
 
     /**
-     * Call this view's OnClickListener, if it is defined.
+     * Call this view's OnClickListener, if it is defined.  Performs all normal
+     * actions associated with clicking: reporting accessibility event, playing
+     * a sound, etc.
      *
      * @return True there was an assigned OnClickListener that was called, false
      *         otherwise is returned.
@@ -3475,9 +3504,10 @@
     public boolean performClick() {
         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
 
-        if (mOnClickListener != null) {
+        ListenerInfo li = mListenerInfo;
+        if (li != null && li.mOnClickListener != null) {
             playSoundEffect(SoundEffectConstants.CLICK);
-            mOnClickListener.onClick(this);
+            li.mOnClickListener.onClick(this);
             return true;
         }
 
@@ -3485,6 +3515,23 @@
     }
 
     /**
+     * Directly call any attached OnClickListener.  Unlike {@link #performClick()},
+     * this only calls the listener, and does not do any associated clicking
+     * actions like reporting an accessibility event.
+     *
+     * @return True there was an assigned OnClickListener that was called, false
+     *         otherwise is returned.
+     */
+    public boolean callOnClick() {
+        ListenerInfo li = mListenerInfo;
+        if (li != null && li.mOnClickListener != null) {
+            li.mOnClickListener.onClick(this);
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Call this view's OnLongClickListener, if it is defined. Invokes the context menu if the
      * OnLongClickListener did not consume the event.
      *
@@ -3494,8 +3541,9 @@
         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
 
         boolean handled = false;
-        if (mOnLongClickListener != null) {
-            handled = mOnLongClickListener.onLongClick(View.this);
+        ListenerInfo li = mListenerInfo;
+        if (li != null && li.mOnLongClickListener != null) {
+            handled = li.mOnLongClickListener.onLongClick(View.this);
         }
         if (!handled) {
             handled = showContextMenu();
@@ -3563,7 +3611,7 @@
      * @param l the key listener to attach to this view
      */
     public void setOnKeyListener(OnKeyListener l) {
-        mOnKeyListener = l;
+        getListenerInfo().mOnKeyListener = l;
     }
 
     /**
@@ -3571,7 +3619,7 @@
      * @param l the touch listener to attach to this view
      */
     public void setOnTouchListener(OnTouchListener l) {
-        mOnTouchListener = l;
+        getListenerInfo().mOnTouchListener = l;
     }
 
     /**
@@ -3579,7 +3627,7 @@
      * @param l the generic motion listener to attach to this view
      */
     public void setOnGenericMotionListener(OnGenericMotionListener l) {
-        mOnGenericMotionListener = l;
+        getListenerInfo().mOnGenericMotionListener = l;
     }
 
     /**
@@ -3587,7 +3635,7 @@
      * @param l the hover listener to attach to this view
      */
     public void setOnHoverListener(OnHoverListener l) {
-        mOnHoverListener = l;
+        getListenerInfo().mOnHoverListener = l;
     }
 
     /**
@@ -3598,7 +3646,7 @@
      * @param l An implementation of {@link android.view.View.OnDragListener}.
      */
     public void setOnDragListener(OnDragListener l) {
-        mOnDragListener = l;
+        getListenerInfo().mOnDragListener = l;
     }
 
     /**
@@ -3804,8 +3852,9 @@
         }
 
         invalidate(true);
-        if (mOnFocusChangeListener != null) {
-            mOnFocusChangeListener.onFocusChange(this, gainFocus);
+        ListenerInfo li = mListenerInfo;
+        if (li != null && li.mOnFocusChangeListener != null) {
+            li.mOnFocusChangeListener.onFocusChange(this, gainFocus);
         }
 
         if (mAttachInfo != null) {
@@ -5439,8 +5488,9 @@
 
         // Give any attached key listener a first crack at the event.
         //noinspection SimplifiableIfStatement
-        if (mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
-                && mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
+        ListenerInfo li = mListenerInfo;
+        if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
+                && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
             return true;
         }
 
@@ -5479,8 +5529,9 @@
 
         if (onFilterTouchEventForSecurity(event)) {
             //noinspection SimplifiableIfStatement
-            if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
-                    mOnTouchListener.onTouch(this, event)) {
+            ListenerInfo li = mListenerInfo;
+            if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
+                    && li.mOnTouchListener.onTouch(this, event)) {
                 return true;
             }
 
@@ -5572,8 +5623,10 @@
 
     private boolean dispatchGenericMotionEventInternal(MotionEvent event) {
         //noinspection SimplifiableIfStatement
-        if (mOnGenericMotionListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
-                && mOnGenericMotionListener.onGenericMotion(this, event)) {
+        ListenerInfo li = mListenerInfo;
+        if (li != null && li.mOnGenericMotionListener != null
+                && (mViewFlags & ENABLED_MASK) == ENABLED
+                && li.mOnGenericMotionListener.onGenericMotion(this, event)) {
             return true;
         }
 
@@ -5599,8 +5652,10 @@
      */
     protected boolean dispatchHoverEvent(MotionEvent event) {
         //noinspection SimplifiableIfStatement
-        if (mOnHoverListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
-                && mOnHoverListener.onHover(this, event)) {
+        ListenerInfo li = mListenerInfo;
+        if (li != null && li.mOnHoverListener != null
+                && (mViewFlags & ENABLED_MASK) == ENABLED
+                && li.mOnHoverListener.onHover(this, event)) {
             return true;
         }
 
@@ -5884,7 +5939,8 @@
                 mAttachInfo.mKeepScreenOn = true;
             }
             mAttachInfo.mSystemUiVisibility |= mSystemUiVisibility;
-            if (mOnSystemUiVisibilityChangeListener != null) {
+            ListenerInfo li = mListenerInfo;
+            if (li != null && li.mOnSystemUiVisibilityChangeListener != null) {
                 mAttachInfo.mHasSystemUiListeners = true;
             }
         }
@@ -6118,8 +6174,9 @@
         ((MenuBuilder)menu).setCurrentMenuInfo(menuInfo);
 
         onCreateContextMenu(menu);
-        if (mOnCreateContextMenuListener != null) {
-            mOnCreateContextMenuListener.onCreateContextMenu(menu, this, menuInfo);
+        ListenerInfo li = mListenerInfo;
+        if (li != null && li.mOnCreateContextMenuListener != null) {
+            li.mOnCreateContextMenuListener.onCreateContextMenu(menu, this, menuInfo);
         }
 
         // Clear the extra information so subsequent items that aren't mine don't
@@ -9723,8 +9780,9 @@
         performCollectViewAttributes(visibility);
         onAttachedToWindow();
 
+        ListenerInfo li = mListenerInfo;
         final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
-                mOnAttachStateChangeListeners;
+                li != null ? li.mOnAttachStateChangeListeners : null;
         if (listeners != null && listeners.size() > 0) {
             // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
             // perform the dispatching. The iterator is a safe guard against listeners that
@@ -9756,8 +9814,9 @@
 
         onDetachedFromWindow();
 
+        ListenerInfo li = mListenerInfo;
         final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
-                mOnAttachStateChangeListeners;
+                li != null ? li.mOnAttachStateChangeListeners : null;
         if (listeners != null && listeners.size() > 0) {
             // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
             // perform the dispatching. The iterator is a safe guard against listeners that
@@ -11193,9 +11252,10 @@
             onLayout(changed, l, t, r, b);
             mPrivateFlags &= ~LAYOUT_REQUIRED;
 
-            if (mOnLayoutChangeListeners != null) {
+            ListenerInfo li = mListenerInfo;
+            if (li != null && li.mOnLayoutChangeListeners != null) {
                 ArrayList<OnLayoutChangeListener> listenersCopy =
-                        (ArrayList<OnLayoutChangeListener>) mOnLayoutChangeListeners.clone();
+                        (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
                 int numListeners = listenersCopy.size();
                 for (int i = 0; i < numListeners; ++i) {
                     listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
@@ -13065,7 +13125,7 @@
      * @param l  The {@link OnSystemUiVisibilityChangeListener} to receive callbacks.
      */
     public void setOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener l) {
-        mOnSystemUiVisibilityChangeListener = l;
+        getListenerInfo().mOnSystemUiVisibilityChangeListener = l;
         if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
             mParent.recomputeViewAttributes(this);
         }
@@ -13076,8 +13136,9 @@
      * the view hierarchy.
      */
     public void dispatchSystemUiVisibilityChanged(int visibility) {
-        if (mOnSystemUiVisibilityChangeListener != null) {
-            mOnSystemUiVisibilityChangeListener.onSystemUiVisibilityChange(
+        ListenerInfo li = mListenerInfo;
+        if (li != null && li.mOnSystemUiVisibilityChangeListener != null) {
+            li.mOnSystemUiVisibilityChangeListener.onSystemUiVisibilityChange(
                     visibility & PUBLIC_STATUS_BAR_VISIBILITY_MASK);
         }
     }
@@ -13349,8 +13410,9 @@
      */
     public boolean dispatchDragEvent(DragEvent event) {
         //noinspection SimplifiableIfStatement
-        if (mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
-                && mOnDragListener.onDrag(this, event)) {
+        ListenerInfo li = mListenerInfo;
+        if (li != null && li.mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
+                && li.mOnDragListener.onDrag(this, event)) {
             return true;
         }
         return onDragEvent(event);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 62b20b3..9b0cd25 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -352,7 +352,7 @@
     private static final int ARRAY_CAPACITY_INCREMENT = 12;
 
     // Used to draw cached views
-    private final Paint mCachePaint = new Paint();
+    private Paint mCachePaint;
 
     // Used to animate add/remove changes in layout
     private LayoutTransition mTransition;
@@ -405,8 +405,6 @@
         mChildren = new View[ARRAY_INITIAL_CAPACITY];
         mChildrenCount = 0;
 
-        mCachePaint.setDither(false);
-
         mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
     }
 
@@ -2909,6 +2907,11 @@
 
             if (layerType == LAYER_TYPE_NONE) {
                 cachePaint = mCachePaint;
+                if (cachePaint == null) {
+                    cachePaint = new Paint();
+                    cachePaint.setDither(false);
+                    mCachePaint = cachePaint;
+                }
                 if (alpha < 1.0f) {
                     cachePaint.setAlpha((int) (alpha * 255));
                     mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9257534..a8680d4 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -271,7 +271,7 @@
     private static final int SIGNED = 2;
     private static final int DECIMAL = 4;
 
-    class Drawables {
+    static class Drawables {
         final Rect mCompoundRect = new Rect();
         Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight,
                 mDrawableStart, mDrawableEnd;
@@ -304,7 +304,7 @@
 
     private int mMarqueeRepeatLimit = 3;
 
-    class InputContentType {
+    static class InputContentType {
         int imeOptions = EditorInfo.IME_NULL;
         String privateImeOptions;
         CharSequence imeActionLabel;
@@ -315,7 +315,7 @@
     }
     InputContentType mInputContentType;
 
-    class InputMethodState {
+    static class InputMethodState {
         Rect mCursorRectInWindow = new Rect();
         RectF mTmpRectF = new RectF();
         float[] mTmpOffset = new float[2];
@@ -5363,7 +5363,7 @@
                     // don't let it be inserted into the text.
                     if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0
                             || shouldAdvanceFocusOnEnter()) {
-                        if (mOnClickListener != null) {
+                        if (hasOnClickListeners()) {
                             return 0;
                         }
                         return -1;
@@ -5497,7 +5497,7 @@
                      * call performClick(), but that won't do anything in
                      * this case.)
                      */
-                    if (mOnClickListener == null) {
+                    if (hasOnClickListeners()) {
                         if (mMovement != null && mText instanceof Editable
                                 && mLayout != null && onCheckIsTextEditor()) {
                             InputMethodManager imm = InputMethodManager.peekInstance();
@@ -5535,7 +5535,7 @@
                          * call performClick(), but that won't do anything in
                          * this case.)
                          */
-                        if (mOnClickListener == null) {
+                        if (hasOnClickListeners()) {
                             View v = focusSearch(FOCUS_DOWN);
 
                             if (v != null) {
diff --git a/core/java/android/widget/ZoomButton.java b/core/java/android/widget/ZoomButton.java
index c5fa18c..eb372ca 100644
--- a/core/java/android/widget/ZoomButton.java
+++ b/core/java/android/widget/ZoomButton.java
@@ -29,8 +29,8 @@
     private final Handler mHandler;
     private final Runnable mRunnable = new Runnable() {
         public void run() {
-            if ((mOnClickListener != null) && mIsInLongpress && isEnabled()) {
-                mOnClickListener.onClick(ZoomButton.this);
+            if (hasOnClickListeners() && mIsInLongpress && isEnabled()) {
+                callOnClick();
                 mHandler.postDelayed(this, mZoomSpeed);
             }
         }
diff --git a/tests/FrameworkPerf/AndroidManifest.xml b/tests/FrameworkPerf/AndroidManifest.xml
index c3fc837..aa663f3 100644
--- a/tests/FrameworkPerf/AndroidManifest.xml
+++ b/tests/FrameworkPerf/AndroidManifest.xml
@@ -3,7 +3,7 @@
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-sdk android:minSdkVersion="5" />
 
-    <application>
+    <application android:hardwareAccelerated="false">
         <activity android:name="FrameworkPerfActivity" android:label="Framework Perf">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/tests/FrameworkPerf/res/layout/main.xml b/tests/FrameworkPerf/res/layout/main.xml
index 8b54118..62b1a7a 100644
--- a/tests/FrameworkPerf/res/layout/main.xml
+++ b/tests/FrameworkPerf/res/layout/main.xml
@@ -64,6 +64,23 @@
         android:orientation="horizontal"
         android:layout_marginTop="10dp"
         >
+        <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:text="Test time (ms): "
+            />
+        <EditText android:id="@+id/testtime"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:inputType="number"
+            android:text="5000"
+        />
+    </LinearLayout>
+
+    <LinearLayout android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:layout_marginTop="10dp"
+        >
         <Button android:id="@+id/start"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
index 66e788c..5e15224 100644
--- a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
+++ b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
@@ -58,6 +58,7 @@
     Spinner mFgSpinner;
     Spinner mBgSpinner;
     TextView mLog;
+    TextView mTestTime;
     PowerManager.WakeLock mPartialWakeLock;
 
     long mMaxRunTime = 5000;
@@ -110,6 +111,7 @@
     };
 
     final Op[] mAvailOps = new Op[] {
+            null,
             new NoOp(),
             new CpuOp(),
             new SchedulerOp(),
@@ -122,6 +124,8 @@
             new ReadFileOp(),
             new ParseXmlResOp(),
             new ParseLargeXmlResOp(),
+            new LayoutInflaterOp(),
+            new LayoutInflaterLargeOp(),
             new LoadSmallBitmapOp(),
             new LoadLargeBitmapOp(),
             new LoadSmallScaledBitmapOp(),
@@ -170,11 +174,14 @@
         mAvailOpDescriptions = new String[mAvailOps.length];
         for (int i=0; i<mAvailOps.length; i++) {
             Op op = mAvailOps[i];
-            if (op.getClass() == NoOp.class) {
+            if (op == null) {
                 mAvailOpLabels[i] = "All";
                 mAvailOpDescriptions[i] = "All tests";
             } else {
                 mAvailOpLabels[i] = op.getName();
+                if (mAvailOpLabels[i] == null) {
+                    mAvailOpLabels[i] = "Nothing";
+                }
                 mAvailOpDescriptions[i] = op.getLongName();
             }
         }
@@ -211,6 +218,7 @@
                 stopRunning();
             }
         });
+        mTestTime = (TextView)findViewById(R.id.testtime);
         mLog = (TextView)findViewById(R.id.log);
 
         PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
@@ -221,11 +229,7 @@
     @Override
     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
         if (parent == mFgSpinner || parent == mBgSpinner) {
-            Spinner spinner = (Spinner)parent;
             Op op = mAvailOps[position];
-            if (op.getClass() == NoOp.class) {
-                op = null;
-            }
             if (parent == mFgSpinner) {
                 mFgTest = op;
                 ((TextView)findViewById(R.id.fgtext)).setText(mAvailOpDescriptions[position]);
@@ -238,8 +242,6 @@
 
     @Override
     public void onNothingSelected(AdapterView<?> parent) {
-        // TODO Auto-generated method stub
-        
     }
 
     @Override
@@ -314,6 +316,7 @@
             updateWakeLock();
             startService(new Intent(this, SchedulerService.class));
             mCurOpIndex = 0;
+            mMaxRunTime = Integer.parseInt(mTestTime.getText().toString());
             mResults.clear();
             startCurOp();
         }