Fix IOOBE due to internal list being updated from remove

This CL removes all prepareChildren(), leaving
only the one in onMeasure(). This makes the state
more stable since we will only update it within a
layout pass. This also reduces any unnecessary work.

BUG: 30252077
BUG: 31303652

Change-Id: Ibd00a3c3ede1dd654b0ed6d1d8d8d1c8da94e57a
(cherry picked from commit 2f517207503fdffbcf930584a37b4a03392755d6)
diff --git a/design/src/android/support/design/widget/CoordinatorLayout.java b/design/src/android/support/design/widget/CoordinatorLayout.java
index ac212f5..cbbe9e1 100644
--- a/design/src/android/support/design/widget/CoordinatorLayout.java
+++ b/design/src/android/support/design/widget/CoordinatorLayout.java
@@ -624,13 +624,7 @@
         return result;
     }
 
-    private void prepareChildren(final boolean forceRefresh) {
-        if (!forceRefresh && mChildDag.size() == getChildCount()
-                && mChildDag.size() == mDependencySortedChildren.size()) {
-            // If we're not being forced and everything looks good, lets skip the call
-            return;
-        }
-
+    private void prepareChildren() {
         mDependencySortedChildren.clear();
         mChildDag.clear();
 
@@ -709,7 +703,7 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        prepareChildren(true);
+        prepareChildren();
         ensurePreDrawListener();
 
         final int paddingLeft = getPaddingLeft();
@@ -1401,12 +1395,10 @@
      * @param view the View to find dependents of to dispatch the call.
      */
     public void dispatchDependentViewsChanged(View view) {
-        prepareChildren(false);
-
         final List<View> dependents = mChildDag.getIncomingEdges(view);
         if (dependents != null && !dependents.isEmpty()) {
             for (int i = 0; i < dependents.size(); i++) {
-                final View child = (View) dependents.get(i);
+                final View child = dependents.get(i);
                 CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)
                         child.getLayoutParams();
                 CoordinatorLayout.Behavior b = lp.getBehavior();
@@ -1427,8 +1419,6 @@
      */
     @NonNull
     public List<View> getDependencies(@NonNull View child) {
-        prepareChildren(false);
-
         final List<View> dependencies = mChildDag.getOutgoingEdges(child);
         mTempDependenciesList.clear();
         if (dependencies != null) {
@@ -1447,8 +1437,6 @@
      */
     @NonNull
     public List<View> getDependents(@NonNull View child) {
-        prepareChildren(false);
-
         final List<View> edges = mChildDag.getIncomingEdges(child);
         mTempDependenciesList.clear();
         if (edges != null) {
@@ -1459,7 +1447,7 @@
 
     @VisibleForTesting
     final List<View> getDependencySortedChildren() {
-        prepareChildren(true);
+        prepareChildren();
         return Collections.unmodifiableList(mDependencySortedChildren);
     }
 
@@ -1489,8 +1477,7 @@
     /**
      * Check if the given child has any layout dependencies on other child views.
      */
-    boolean hasDependencies(View child) {
-        prepareChildren(false);
+    private boolean hasDependencies(View child) {
         return mChildDag.hasOutgoingEdges(child);
     }
 
diff --git a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
index 5471c38..f6350c1 100644
--- a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
@@ -293,4 +293,45 @@
         // And assert that View B's Behavior was called appropriately
         verify(behavior, times(1)).onDependentViewRemoved(col, viewB, viewA);
     }
+
+    @Test
+    public void testGetDependenciesAfterDependentViewRemoved() {
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
+
+        // Add two views, A & B, where B depends on A
+        final View viewA = new View(col.getContext());
+        final View viewB = new View(col.getContext());
+        final CoordinatorLayout.LayoutParams lpB = col.generateDefaultLayoutParams();
+        final CoordinatorLayout.Behavior behavior
+                = new CoordinatorLayoutUtils.DependentBehavior(viewA) {
+            @Override
+            public void onDependentViewRemoved(CoordinatorLayout parent, View child,
+                    View dependency) {
+                parent.getDependencies(child);
+            }
+        };
+        lpB.setBehavior(behavior);
+
+        // Now add views
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                col.addView(viewA);
+                col.addView(viewB, lpB);
+            }
+        });
+
+        // Wait for a layout
+        instrumentation.waitForIdleSync();
+
+        // Now remove view A, which will trigger onDependentViewRemoved() on view B's behavior
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                col.removeView(viewA);
+            }
+        });
+    }
+
 }