Throw if onCreateViewHolder returns attached view

Bug: 38496391
Bug: 38375597
Test: RecyclerViewBasicTest

RecyclerView doesn't support pre-attached ViewHolders. Previously this
failure would occur in a racey way, depending on how the ViewHolder is
used.

Change-Id: I74b48405fab07e0abb0012726289d772ed93793e
diff --git a/v7/recyclerview/src/main/java/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/main/java/android/support/v7/widget/RecyclerView.java
index a287979..41b3627 100644
--- a/v7/recyclerview/src/main/java/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/main/java/android/support/v7/widget/RecyclerView.java
@@ -6643,11 +6643,19 @@
          * @see #onCreateViewHolder(ViewGroup, int)
          */
         public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
-            TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
-            final VH holder = onCreateViewHolder(parent, viewType);
-            holder.mItemViewType = viewType;
-            TraceCompat.endSection();
-            return holder;
+            try {
+                TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
+                final VH holder = onCreateViewHolder(parent, viewType);
+                if (holder.itemView.getParent() != null) {
+                    throw new IllegalStateException("ViewHolder views must not be attached when"
+                            + " created. Ensure that you are not passing 'true' to the attachToRoot"
+                            + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
+                }
+                holder.mItemViewType = viewType;
+                return holder;
+            } finally {
+                TraceCompat.endSection();
+            }
         }
 
         /**
diff --git a/v7/recyclerview/tests/res/layout/item_view.xml b/v7/recyclerview/tests/res/layout/item_view.xml
index ffb2153..3addcfd 100644
--- a/v7/recyclerview/tests/res/layout/item_view.xml
+++ b/v7/recyclerview/tests/res/layout/item_view.xml
@@ -15,6 +15,7 @@
   ~ limitations under the License.
   -->
 <View xmlns:android="http://schemas.android.com/apk/res/android"
+      android:id="@+id/item_view"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:background="@drawable/item_bg"
@@ -22,4 +23,4 @@
       android:focusableInTouchMode="true"
       android:orientation="vertical">
 
-</View>
\ No newline at end of file
+</View>
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
index 3357c2f..50e2d8b 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
@@ -31,11 +31,14 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.support.annotation.NonNull;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.recyclerview.test.R;
 import android.util.AttributeSet;
 import android.util.SparseArray;
+import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -66,7 +69,7 @@
     }
 
     private Context getContext() {
-        return InstrumentationRegistry.getContext();
+        return InstrumentationRegistry.getTargetContext();
     }
 
     @Test
@@ -367,6 +370,40 @@
     }
 
     @Test
+    public void createAttachedException() {
+        mRecyclerView.setAdapter(new RecyclerView.Adapter() {
+            @NonNull
+            @Override
+            public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
+                    int viewType) {
+                View view = LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.item_view, parent, true)
+                        .findViewById(R.id.item_view); // find child, since parent is returned
+                return new RecyclerView.ViewHolder(view) {};
+            }
+
+            @Override
+            public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
+                fail("shouldn't get here, should throw during create");
+            }
+
+            @Override
+            public int getItemCount() {
+                return 1;
+            }
+        });
+        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+
+        try {
+            measure();
+            //layout();
+            fail("IllegalStateException expected");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+    }
+
+    @Test
     public void prefetchChangesCacheSize() {
         mRecyclerView.setAdapter(new MockAdapter(20));
         MockLayoutManager mlm = new MockLayoutManager() {
@@ -790,4 +827,4 @@
         void addItems(int start, int count) {
         }
     }
-}
\ No newline at end of file
+}