Merge "Fixing a memory leak in ViewRootImpl and a focus change callback issue."
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 97287c3..a4e4f37 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4376,10 +4376,16 @@
         if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {
             mPrivateFlags |= PFLAG_FOCUSED;
 
+            View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;
+
             if (mParent != null) {
                 mParent.requestChildFocus(this, this);
             }
 
+            if (mAttachInfo != null) {
+                mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);
+            }
+
             onFocusChanged(true, direction, previouslyFocusedRect);
             refreshDrawableState();
 
@@ -4486,7 +4492,9 @@
 
             refreshDrawableState();
 
-            ensureInputFocusOnFirstFocusable();
+            if (!rootViewRequestFocus()) {
+                notifyGlobalFocusCleared(this);
+            }
 
             if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                 notifyAccessibilityStateChanged();
@@ -4494,11 +4502,18 @@
         }
     }
 
-    void ensureInputFocusOnFirstFocusable() {
+    void notifyGlobalFocusCleared(View oldFocus) {
+        if (oldFocus != null && mAttachInfo != null) {
+            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
+        }
+    }
+
+    boolean rootViewRequestFocus() {
         View root = getRootView();
         if (root != null) {
-            root.requestFocus();
+            return root.requestFocus();
         }
+        return false;
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c274f27..a4898fc 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3697,7 +3697,9 @@
             clearChildFocus = true;
         }
 
-        view.clearAccessibilityFocus();
+        if (view.isAccessibilityFocused()) {
+            view.clearAccessibilityFocus();
+        }
 
         cancelTouchTarget(view);
         cancelHoverTarget(view);
@@ -3719,11 +3721,9 @@
 
         if (clearChildFocus) {
             clearChildFocus(view);
-            ensureInputFocusOnFirstFocusable();
-        }
-
-        if (view.isAccessibilityFocused()) {
-            view.clearAccessibilityFocus();
+            if (!rootViewRequestFocus()) {
+                notifyGlobalFocusCleared(this);
+            }
         }
 
         onViewRemoved(view);
@@ -3765,7 +3765,7 @@
     private void removeViewsInternal(int start, int count) {
         final View focused = mFocused;
         final boolean detach = mAttachInfo != null;
-        View clearChildFocus = null;
+        boolean clearChildFocus = false;
 
         final View[] children = mChildren;
         final int end = start + count;
@@ -3779,10 +3779,12 @@
 
             if (view == focused) {
                 view.unFocus();
-                clearChildFocus = view;
+                clearChildFocus = true;
             }
 
-            view.clearAccessibilityFocus();
+            if (view.isAccessibilityFocused()) {
+                view.clearAccessibilityFocus();
+            }
 
             cancelTouchTarget(view);
             cancelHoverTarget(view);
@@ -3805,9 +3807,11 @@
 
         removeFromArray(start, count);
 
-        if (clearChildFocus != null) {
-            clearChildFocus(clearChildFocus);
-            ensureInputFocusOnFirstFocusable();
+        if (clearChildFocus) {
+            clearChildFocus(focused);
+            if (!rootViewRequestFocus()) {
+                notifyGlobalFocusCleared(focused);
+            }
         }
     }
 
@@ -3849,7 +3853,7 @@
 
         final View focused = mFocused;
         final boolean detach = mAttachInfo != null;
-        View clearChildFocus = null;
+        boolean clearChildFocus = false;
 
         needGlobalAttributesUpdate(false);
 
@@ -3862,10 +3866,12 @@
 
             if (view == focused) {
                 view.unFocus();
-                clearChildFocus = view;
+                clearChildFocus = true;
             }
 
-            view.clearAccessibilityFocus();
+            if (view.isAccessibilityFocused()) {
+                view.clearAccessibilityFocus();
+            }
 
             cancelTouchTarget(view);
             cancelHoverTarget(view);
@@ -3887,9 +3893,11 @@
             children[i] = null;
         }
 
-        if (clearChildFocus != null) {
-            clearChildFocus(clearChildFocus);
-            ensureInputFocusOnFirstFocusable();
+        if (clearChildFocus) {
+            clearChildFocus(focused);
+            if (!rootViewRequestFocus()) {
+                notifyGlobalFocusCleared(focused);
+            }
         }
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f9f955e..6a96893 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -169,9 +169,6 @@
     int mSeq;
 
     View mView;
-    View mFocusedView;
-    View mRealFocusedView;  // this is not set to null in touch mode
-    View mOldFocusedView;
 
     View mAccessibilityFocusedHost;
     AccessibilityNodeInfo mAccessibilityFocusedVirtualView;
@@ -269,7 +266,7 @@
 
     boolean mScrollMayChange;
     int mSoftInputMode;
-    View mLastScrolledFocus;
+    WeakReference<View> mLastScrolledFocus;
     int mScrollY;
     int mCurScrollY;
     Scroller mScroller;
@@ -1526,7 +1523,9 @@
                 } else if (!mSurface.isValid()) {
                     // If the surface has been removed, then reset the scroll
                     // positions.
-                    mLastScrolledFocus = null;
+                    if (mLastScrolledFocus != null) {
+                        mLastScrolledFocus.clear();
+                    }
                     mScrollY = mCurScrollY = 0;
                     if (mScroller != null) {
                         mScroller.abortAnimation();
@@ -1802,13 +1801,11 @@
             if (mView != null) {
                 if (!mView.hasFocus()) {
                     mView.requestFocus(View.FOCUS_FORWARD);
-                    mFocusedView = mRealFocusedView = mView.findFocus();
                     if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
-                            + mFocusedView);
+                            + mView.findFocus());
                 } else {
-                    mRealFocusedView = mView.findFocus();
                     if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
-                            + mRealFocusedView);
+                            + mView.findFocus());
                 }
             }
             if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0) {
@@ -2514,17 +2511,12 @@
             // requestChildRectangleOnScreen() call (in which case 'rectangle'
             // is non-null and we just want to scroll to whatever that
             // rectangle is).
-            View focus = mRealFocusedView;
-
-            // When in touch mode, focus points to the previously focused view,
-            // which may have been removed from the view hierarchy. The following
-            // line checks whether the view is still in our hierarchy.
-            if (focus == null || focus.mAttachInfo != mAttachInfo) {
-                mRealFocusedView = null;
+            View focus = mView.findFocus();
+            if (focus == null) {
                 return false;
             }
-
-            if (focus != mLastScrolledFocus) {
+            View lastScrolledFocus = (mLastScrolledFocus != null) ? mLastScrolledFocus.get() : null;
+            if (lastScrolledFocus != null && focus != lastScrolledFocus) {
                 // If the focus has changed, then ignore any requests to scroll
                 // to a rectangle; first we want to make sure the entire focus
                 // view is visible.
@@ -2533,8 +2525,7 @@
             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus
                     + " rectangle=" + rectangle + " ci=" + ci
                     + " vi=" + vi);
-            if (focus == mLastScrolledFocus && !mScrollMayChange
-                    && rectangle == null) {
+            if (focus == lastScrolledFocus && !mScrollMayChange && rectangle == null) {
                 // Optimization: if the focus hasn't changed since last
                 // time, and no layout has happened, then just leave things
                 // as they are.
@@ -2544,7 +2535,7 @@
                 // We need to determine if the currently focused view is
                 // within the visible part of the window and, if not, apply
                 // a pan so it can be seen.
-                mLastScrolledFocus = focus;
+                mLastScrolledFocus = new WeakReference<View>(focus);
                 mScrollMayChange = false;
                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?");
                 // Try to find the rectangle from the focus view.
@@ -2671,33 +2662,19 @@
     }
 
     public void requestChildFocus(View child, View focused) {
-        checkThread();
-
         if (DEBUG_INPUT_RESIZE) {
             Log.v(TAG, "Request child focus: focus now " + focused);
         }
-
-        mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, focused);
+        checkThread();
         scheduleTraversals();
-
-        mFocusedView = mRealFocusedView = focused;
     }
 
     public void clearChildFocus(View child) {
-        checkThread();
-
         if (DEBUG_INPUT_RESIZE) {
             Log.v(TAG, "Clearing child focus");
         }
-
-        mOldFocusedView = mFocusedView;
-
-        // Invoke the listener only if there is no view to take focus
-        if (focusSearch(null, View.FOCUS_FORWARD) == null) {
-            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, null);
-        }
-
-        mFocusedView = mRealFocusedView = null;
+        checkThread();
+        scheduleTraversals();
     }
 
     @Override
@@ -2714,14 +2691,13 @@
                 // the one case where will transfer focus away from the current one
                 // is if the current view is a view group that prefers to give focus
                 // to its children first AND the view is a descendant of it.
-                mFocusedView = mView.findFocus();
-                boolean descendantsHaveDibsOnFocus =
-                        (mFocusedView instanceof ViewGroup) &&
-                            (((ViewGroup) mFocusedView).getDescendantFocusability() ==
-                                    ViewGroup.FOCUS_AFTER_DESCENDANTS);
-                if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
-                    // If a view gets the focus, the listener will be invoked from requestChildFocus()
-                    v.requestFocus();
+                View focused = mView.findFocus();
+                if (focused instanceof ViewGroup) {
+                    ViewGroup group = (ViewGroup) focused;
+                    if (group.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS
+                            && isViewDescendantOf(v, focused)) {
+                        v.requestFocus();
+                    }
                 }
             }
         }
@@ -3199,7 +3175,6 @@
                 // set yet.
                 final View focused = mView.findFocus();
                 if (focused != null && !focused.isFocusableInTouchMode()) {
-
                     final ViewGroup ancestorToTakeFocus =
                             findAncestorToTakeFocusInTouchMode(focused);
                     if (ancestorToTakeFocus != null) {
@@ -3208,10 +3183,7 @@
                         return ancestorToTakeFocus.requestFocus();
                     } else {
                         // nothing appropriate to have focus in touch mode, clear it out
-                        mView.unFocus();
-                        mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
-                        mFocusedView = null;
-                        mOldFocusedView = null;
+                        focused.unFocus();
                         return true;
                     }
                 }
@@ -3246,12 +3218,11 @@
     private boolean leaveTouchMode() {
         if (mView != null) {
             if (mView.hasFocus()) {
-                // i learned the hard way to not trust mFocusedView :)
-                mFocusedView = mView.findFocus();
-                if (!(mFocusedView instanceof ViewGroup)) {
+                View focusedView = mView.findFocus();
+                if (!(focusedView instanceof ViewGroup)) {
                     // some view has focus, let it keep it
                     return false;
-                } else if (((ViewGroup)mFocusedView).getDescendantFocusability() !=
+                } else if (((ViewGroup) focusedView).getDescendantFocusability() !=
                         ViewGroup.FOCUS_AFTER_DESCENDANTS) {
                     // some view group has focus, and doesn't prefer its children
                     // over itself for focus, so let them keep it.