Merge "Prohibit using recycled AccessibilityNodeInfos."
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 53efe18..ec6797c 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -651,7 +651,7 @@
     private static AtomicInteger sNumInstancesInUse;
 
     /**
-     * Gets the accessibility view id which identifies a View in the view three.
+     * Gets the accessibility view id which identifies a View in the view tree.
      *
      * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
      * @return The accessibility view id part of the node id.
@@ -743,6 +743,7 @@
     private RangeInfo mRangeInfo;
     private CollectionInfo mCollectionInfo;
     private CollectionItemInfo mCollectionItemInfo;
+    private boolean mRecycled;
 
     /**
      * Hide constructor from clients.
@@ -785,6 +786,7 @@
      * @param virtualDescendantId The id of the virtual descendant.
      */
     public void setSource(View root, int virtualDescendantId) {
+        enforceNotRecycled();
         enforceNotSealed();
         mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED_ITEM_ID;
         final int rootAccessibilityViewId =
@@ -804,6 +806,7 @@
      * @see #FOCUS_ACCESSIBILITY
      */
     public AccessibilityNodeInfo findFocus(int focus) {
+        enforceNotRecycled();
         enforceSealed();
         enforceValidFocusType(focus);
         if (!canPerformRequestOverConnection(mSourceNodeId)) {
@@ -828,6 +831,7 @@
      * @return The node info for the view that can take accessibility focus.
      */
     public AccessibilityNodeInfo focusSearch(int direction) {
+        enforceNotRecycled();
         enforceSealed();
         enforceValidFocusDirection(direction);
         if (!canPerformRequestOverConnection(mSourceNodeId)) {
@@ -843,6 +847,7 @@
      * @return The window id.
      */
     public int getWindowId() {
+        enforceNotRecycled();
         return mWindowId;
     }
 
@@ -860,6 +865,7 @@
      * @hide
      */
     public boolean refresh(Bundle arguments, boolean bypassCache) {
+        enforceNotRecycled();
         enforceSealed();
         if (!canPerformRequestOverConnection(mSourceNodeId)) {
             return false;
@@ -923,6 +929,7 @@
      * @hide
      */
     public long getChildId(int index) {
+        enforceNotRecycled();
         if (mChildNodeIds == null) {
             throw new IndexOutOfBoundsException();
         }
@@ -935,6 +942,7 @@
      * @return The child count.
      */
     public int getChildCount() {
+        enforceNotRecycled();
         return mChildNodeIds == null ? 0 : mChildNodeIds.size();
     }
 
@@ -953,6 +961,7 @@
      *
      */
     public AccessibilityNodeInfo getChild(int index) {
+        enforceNotRecycled();
         enforceSealed();
         if (mChildNodeIds == null) {
             return null;
@@ -1029,6 +1038,7 @@
     }
 
     private void addChildInternal(View root, int virtualDescendantId, boolean checked) {
+        enforceNotRecycled();
         enforceNotSealed();
         if (mChildNodeIds == null) {
             mChildNodeIds = new LongArray();
@@ -1054,6 +1064,7 @@
      * @see #addChild(View, int)
      */
     public boolean removeChild(View root, int virtualDescendantId) {
+        enforceNotRecycled();
         enforceNotSealed();
         final LongArray childIds = mChildNodeIds;
         if (childIds == null) {
@@ -1074,6 +1085,7 @@
      * Gets the actions that can be performed on the node.
      */
     public List<AccessibilityAction> getActionList() {
+        enforceNotRecycled();
         return CollectionUtils.emptyIfNull(mActions);
     }
 
@@ -1101,6 +1113,7 @@
      */
     @Deprecated
     public int getActions() {
+        enforceNotRecycled();
         int returnValue = 0;
 
         if (mActions == null) {
@@ -1139,6 +1152,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void addAction(AccessibilityAction action) {
+        enforceNotRecycled();
         enforceNotSealed();
 
         addActionUnchecked(action);
@@ -1174,6 +1188,7 @@
      */
     @Deprecated
     public void addAction(int action) {
+        enforceNotRecycled();
         enforceNotSealed();
 
         if ((action & ACTION_TYPE_MASK) != 0) {
@@ -1200,6 +1215,7 @@
      */
     @Deprecated
     public void removeAction(int action) {
+        enforceNotRecycled();
         enforceNotSealed();
 
         removeAction(getActionSingleton(action));
@@ -1220,6 +1236,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public boolean removeAction(AccessibilityAction action) {
+        enforceNotRecycled();
         enforceNotSealed();
 
         if (mActions == null || action == null) {
@@ -1235,6 +1252,7 @@
      * @hide
      */
     public void removeAllActions() {
+        enforceNotRecycled();
         if (mActions != null) {
             mActions.clear();
         }
@@ -1250,6 +1268,7 @@
      * @see #setTraversalBefore(android.view.View, int)
      */
     public AccessibilityNodeInfo getTraversalBefore() {
+        enforceNotRecycled();
         enforceSealed();
         return getNodeForAccessibilityId(mTraversalBefore);
     }
@@ -1294,6 +1313,7 @@
      * @param virtualDescendantId The id of the virtual descendant.
      */
     public void setTraversalBefore(View root, int virtualDescendantId) {
+        enforceNotRecycled();
         enforceNotSealed();
         final int rootAccessibilityViewId = (root != null)
                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
@@ -1311,6 +1331,7 @@
      * @see #setTraversalAfter(android.view.View, int)
      */
     public AccessibilityNodeInfo getTraversalAfter() {
+        enforceNotRecycled();
         enforceSealed();
         return getNodeForAccessibilityId(mTraversalAfter);
     }
@@ -1354,6 +1375,7 @@
      * @param virtualDescendantId The id of the virtual descendant.
      */
     public void setTraversalAfter(View root, int virtualDescendantId) {
+        enforceNotRecycled();
         enforceNotSealed();
         final int rootAccessibilityViewId = (root != null)
                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
@@ -1371,6 +1393,7 @@
      * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
      */
     public List<String> getAvailableExtraData() {
+        enforceNotRecycled();
         if (mExtraDataKeys != null) {
             return Collections.unmodifiableList(mExtraDataKeys);
         } else {
@@ -1395,6 +1418,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setAvailableExtraData(List<String> extraDataKeys) {
+        enforceNotRecycled();
         enforceNotSealed();
         mExtraDataKeys = new ArrayList<>(extraDataKeys);
     }
@@ -1415,6 +1439,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setMaxTextLength(int max) {
+        enforceNotRecycled();
         enforceNotSealed();
         mMaxTextLength = max;
     }
@@ -1426,6 +1451,7 @@
      * @see #setMaxTextLength(int)
      */
     public int getMaxTextLength() {
+        enforceNotRecycled();
         return mMaxTextLength;
     }
 
@@ -1442,6 +1468,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setMovementGranularities(int granularities) {
+        enforceNotRecycled();
         enforceNotSealed();
         mMovementGranularities = granularities;
     }
@@ -1452,6 +1479,7 @@
      * @return The bit mask with granularities.
      */
     public int getMovementGranularities() {
+        enforceNotRecycled();
         return mMovementGranularities;
     }
 
@@ -1468,6 +1496,7 @@
      * @throws IllegalStateException If called outside of an AccessibilityService.
      */
     public boolean performAction(int action) {
+        enforceNotRecycled();
         enforceSealed();
         if (!canPerformRequestOverConnection(mSourceNodeId)) {
             return false;
@@ -1491,6 +1520,7 @@
      * @throws IllegalStateException If called outside of an AccessibilityService.
      */
     public boolean performAction(int action, Bundle arguments) {
+        enforceNotRecycled();
         enforceSealed();
         if (!canPerformRequestOverConnection(mSourceNodeId)) {
             return false;
@@ -1515,6 +1545,7 @@
      * @return A list of node info.
      */
     public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
+        enforceNotRecycled();
         enforceSealed();
         if (!canPerformRequestOverConnection(mSourceNodeId)) {
             return Collections.emptyList();
@@ -1546,6 +1577,7 @@
      * @return A list of node info.
      */
     public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) {
+        enforceNotRecycled();
         enforceSealed();
         if (!canPerformRequestOverConnection(mSourceNodeId)) {
             return Collections.emptyList();
@@ -1563,6 +1595,7 @@
      * @see android.accessibilityservice.AccessibilityService#getWindows()
      */
     public AccessibilityWindowInfo getWindow() {
+        enforceNotRecycled();
         enforceSealed();
         if (!canPerformRequestOverConnection(mSourceNodeId)) {
             return null;
@@ -1582,6 +1615,7 @@
      * @return The parent.
      */
     public AccessibilityNodeInfo getParent() {
+        enforceNotRecycled();
         enforceSealed();
         return getNodeForAccessibilityId(mParentNodeId);
     }
@@ -1631,6 +1665,7 @@
      * @param virtualDescendantId The id of the virtual descendant.
      */
     public void setParent(View root, int virtualDescendantId) {
+        enforceNotRecycled();
         enforceNotSealed();
         final int rootAccessibilityViewId =
             (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
@@ -1643,6 +1678,7 @@
      * @param outBounds The output node bounds.
      */
     public void getBoundsInParent(Rect outBounds) {
+        enforceNotRecycled();
         outBounds.set(mBoundsInParent.left, mBoundsInParent.top,
                 mBoundsInParent.right, mBoundsInParent.bottom);
     }
@@ -1660,6 +1696,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setBoundsInParent(Rect bounds) {
+        enforceNotRecycled();
         enforceNotSealed();
         mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
     }
@@ -1670,6 +1707,7 @@
      * @param outBounds The output node bounds.
      */
     public void getBoundsInScreen(Rect outBounds) {
+        enforceNotRecycled();
         outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top,
                 mBoundsInScreen.right, mBoundsInScreen.bottom);
     }
@@ -1680,6 +1718,7 @@
      * @hide Not safe to expose outside the framework.
      */
     public Rect getBoundsInScreen() {
+        enforceNotRecycled();
         return mBoundsInScreen;
     }
 
@@ -1696,6 +1735,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setBoundsInScreen(Rect bounds) {
+        enforceNotRecycled();
         enforceNotSealed();
         mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
     }
@@ -2037,6 +2077,7 @@
      * @return The drawing position of the view corresponding to this node relative to its siblings.
      */
     public int getDrawingOrder() {
+        enforceNotRecycled();
         return mDrawingOrderInParent;
     }
 
@@ -2052,6 +2093,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setDrawingOrder(int drawingOrderInParent) {
+        enforceNotRecycled();
         enforceNotSealed();
         mDrawingOrderInParent = drawingOrderInParent;
     }
@@ -2063,6 +2105,7 @@
      * @return The collection info.
      */
     public CollectionInfo getCollectionInfo() {
+        enforceNotRecycled();
         return mCollectionInfo;
     }
 
@@ -2078,6 +2121,7 @@
      * @param collectionInfo The collection info.
      */
     public void setCollectionInfo(CollectionInfo collectionInfo) {
+        enforceNotRecycled();
         enforceNotSealed();
         mCollectionInfo = collectionInfo;
     }
@@ -2089,6 +2133,7 @@
      * @return The collection item info.
      */
     public CollectionItemInfo getCollectionItemInfo() {
+        enforceNotRecycled();
         return mCollectionItemInfo;
     }
 
@@ -2102,6 +2147,7 @@
      * </p>
      */
     public void setCollectionItemInfo(CollectionItemInfo collectionItemInfo) {
+        enforceNotRecycled();
         enforceNotSealed();
         mCollectionItemInfo = collectionItemInfo;
     }
@@ -2112,6 +2158,7 @@
      * @return The range.
      */
     public RangeInfo getRangeInfo() {
+        enforceNotRecycled();
         return mRangeInfo;
     }
 
@@ -2126,6 +2173,7 @@
      * @param rangeInfo The range info.
      */
     public void setRangeInfo(RangeInfo rangeInfo) {
+        enforceNotRecycled();
         enforceNotSealed();
         mRangeInfo = rangeInfo;
     }
@@ -2198,6 +2246,7 @@
      * @see android.view.View#getAccessibilityLiveRegion()
      */
     public int getLiveRegion() {
+        enforceNotRecycled();
         return mLiveRegion;
     }
 
@@ -2214,6 +2263,7 @@
      * @see android.view.View#setAccessibilityLiveRegion(int)
      */
     public void setLiveRegion(int mode) {
+        enforceNotRecycled();
         enforceNotSealed();
         mLiveRegion = mode;
     }
@@ -2261,7 +2311,6 @@
      * @param opensPopup If the the node opens a popup.
      */
     public void setCanOpenPopup(boolean opensPopup) {
-        enforceNotSealed();
         setBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP, opensPopup);
     }
 
@@ -2348,6 +2397,7 @@
      * @return The package name.
      */
     public CharSequence getPackageName() {
+        enforceNotRecycled();
         return mPackageName;
     }
 
@@ -2364,6 +2414,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setPackageName(CharSequence packageName) {
+        enforceNotRecycled();
         enforceNotSealed();
         mPackageName = packageName;
     }
@@ -2374,6 +2425,7 @@
      * @return The class name.
      */
     public CharSequence getClassName() {
+        enforceNotRecycled();
         return mClassName;
     }
 
@@ -2390,6 +2442,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setClassName(CharSequence className) {
+        enforceNotRecycled();
         enforceNotSealed();
         mClassName = className;
     }
@@ -2413,6 +2466,7 @@
      * @return The text.
      */
     public CharSequence getText() {
+        enforceNotRecycled();
         // Attach this node to any spans that need it
         if (mText instanceof Spanned) {
             Spanned spanned = (Spanned) mText;
@@ -2435,6 +2489,7 @@
      * @hide
      */
     public CharSequence getOriginalText() {
+        enforceNotRecycled();
         return mOriginalText;
     }
 
@@ -2451,6 +2506,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setText(CharSequence text) {
+        enforceNotRecycled();
         enforceNotSealed();
         mOriginalText = text;
         // Replace any ClickableSpans in mText with placeholders
@@ -2489,6 +2545,7 @@
      * @return The hint text.
      */
     public CharSequence getHintText() {
+        enforceNotRecycled();
         return mHintText;
     }
 
@@ -2505,6 +2562,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setHintText(CharSequence hintText) {
+        enforceNotRecycled();
         enforceNotSealed();
         mHintText = (hintText == null) ? null : hintText.subSequence(0, hintText.length());
     }
@@ -2522,6 +2580,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setError(CharSequence error) {
+        enforceNotRecycled();
         enforceNotSealed();
         mError = (error == null) ? null : error.subSequence(0, error.length());
     }
@@ -2532,6 +2591,7 @@
      * @return The error text.
      */
     public CharSequence getError() {
+        enforceNotRecycled();
         return mError;
     }
 
@@ -2541,6 +2601,7 @@
      * @return The content description.
      */
     public CharSequence getContentDescription() {
+        enforceNotRecycled();
         return mContentDescription;
     }
 
@@ -2557,6 +2618,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setContentDescription(CharSequence contentDescription) {
+        enforceNotRecycled();
         enforceNotSealed();
         mContentDescription = (contentDescription == null) ? null
                 : contentDescription.subSequence(0, contentDescription.length());
@@ -2569,6 +2631,7 @@
      * @param labeled The view for which this info serves as a label.
      */
     public void setLabelFor(View labeled) {
+        enforceNotRecycled();
         setLabelFor(labeled, AccessibilityNodeProvider.HOST_VIEW_ID);
     }
 
@@ -2592,6 +2655,7 @@
      * @param virtualDescendantId The id of the virtual descendant.
      */
     public void setLabelFor(View root, int virtualDescendantId) {
+        enforceNotRecycled();
         enforceNotSealed();
         final int rootAccessibilityViewId = (root != null)
                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
@@ -2610,6 +2674,7 @@
      * @return The labeled info.
      */
     public AccessibilityNodeInfo getLabelFor() {
+        enforceNotRecycled();
         enforceSealed();
         return getNodeForAccessibilityId(mLabelForId);
     }
@@ -2644,6 +2709,7 @@
      * @param virtualDescendantId The id of the virtual descendant.
      */
     public void setLabeledBy(View root, int virtualDescendantId) {
+        enforceNotRecycled();
         enforceNotSealed();
         final int rootAccessibilityViewId = (root != null)
                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
@@ -2662,6 +2728,7 @@
      * @return The label.
      */
     public AccessibilityNodeInfo getLabeledBy() {
+        enforceNotRecycled();
         enforceSealed();
         return getNodeForAccessibilityId(mLabeledById);
     }
@@ -2678,6 +2745,7 @@
      * @param viewIdResName The id resource name.
      */
     public void setViewIdResourceName(String viewIdResName) {
+        enforceNotRecycled();
         enforceNotSealed();
         mViewIdResourceName = viewIdResName;
     }
@@ -2695,6 +2763,7 @@
      * @return The id resource name.
      */
     public String getViewIdResourceName() {
+        enforceNotRecycled();
         return mViewIdResourceName;
     }
 
@@ -2710,6 +2779,7 @@
      *         there is no text selection and no cursor.
      */
     public int getTextSelectionStart() {
+        enforceNotRecycled();
         return mTextSelectionStart;
     }
 
@@ -2725,6 +2795,7 @@
      *         there is no text selection and no cursor.
      */
     public int getTextSelectionEnd() {
+        enforceNotRecycled();
         return mTextSelectionEnd;
     }
 
@@ -2742,6 +2813,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setTextSelection(int start, int end) {
+        enforceNotRecycled();
         enforceNotSealed();
         mTextSelectionStart = start;
         mTextSelectionEnd = end;
@@ -2753,6 +2825,7 @@
      * @return The input type.
      */
     public int getInputType() {
+        enforceNotRecycled();
         return mInputType;
     }
 
@@ -2770,6 +2843,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setInputType(int inputType) {
+        enforceNotRecycled();
         enforceNotSealed();
         mInputType = inputType;
     }
@@ -2788,6 +2862,7 @@
      * @return The bundle.
      */
     public Bundle getExtras() {
+        enforceNotRecycled();
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -2809,6 +2884,7 @@
      * @return The value.
      */
     private boolean getBooleanProperty(int property) {
+        enforceNotRecycled();
         return (mBooleanProperties & property) != 0;
     }
 
@@ -2821,6 +2897,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     private void setBooleanProperty(int property, boolean value) {
+        enforceNotRecycled();
         enforceNotSealed();
         if (value) {
             mBooleanProperties |= property;
@@ -2850,6 +2927,7 @@
      * @hide
      */
     public int getConnectionId() {
+        enforceNotRecycled();
         return mConnectionId;
     }
 
@@ -2870,6 +2948,7 @@
      * @hide
      */
     public void setSourceNodeId(long sourceId, int windowId) {
+        enforceNotRecycled();
         enforceNotSealed();
         mSourceNodeId = sourceId;
         mWindowId = windowId;
@@ -2883,6 +2962,7 @@
      * @hide
      */
     public long getSourceNodeId() {
+        enforceNotRecycled();
         return mSourceNodeId;
     }
 
@@ -2894,6 +2974,7 @@
      * @hide
      */
     public void setSealed(boolean sealed) {
+        enforceNotRecycled();
         mSealed = sealed;
     }
 
@@ -2960,6 +3041,11 @@
         }
     }
 
+    private void enforceNotRecycled() {
+        if (mRecycled) {
+            throw new IllegalStateException("Cannot interact with recycled instance");
+        }
+    }
     /**
      * Returns a cached instance if such is available otherwise a new one
      * and sets the source.
@@ -3001,7 +3087,11 @@
         if (sNumInstancesInUse != null) {
             sNumInstancesInUse.incrementAndGet();
         }
-        return (info != null) ? info : new AccessibilityNodeInfo();
+        if (info != null) {
+            info.mRecycled = false;
+            return info;
+        }
+        return new AccessibilityNodeInfo();
     }
 
     /**
@@ -3027,6 +3117,7 @@
      */
     public void recycle() {
         clear();
+        mRecycled = true;
         sPool.release(this);
         if (sNumInstancesInUse != null) {
             sNumInstancesInUse.decrementAndGet();
@@ -3052,6 +3143,7 @@
      */
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
+        enforceNotRecycled();
         // Write bit set of indices of fields with values differing from default
         long nonDefaultFields = 0;
         int fieldIndex = 0; // index of the current field
@@ -3286,6 +3378,7 @@
      * @param other The other instance.
      */
     private void init(AccessibilityNodeInfo other) {
+        enforceNotRecycled();
         mSealed = other.mSealed;
         mSourceNodeId = other.mSourceNodeId;
         mParentNodeId = other.mParentNodeId;
@@ -3357,6 +3450,7 @@
      * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
      */
     private void initFromParcel(Parcel parcel) {
+        enforceNotRecycled();
         // Bit mask of non-default-valued field indices
         long nonDefaultFields = parcel.readLong();
         int fieldIndex = 0;