handle a race condition where a cursor receives an inflight onChange after the cursor has been closed

Change-Id: I11e3d969ef48ef5150976241bb62717c6c0c1b83
diff --git a/core/java/android/content/ContentQueryMap.java b/core/java/android/content/ContentQueryMap.java
index c955094..8aeaa8f 100644
--- a/core/java/android/content/ContentQueryMap.java
+++ b/core/java/android/content/ContentQueryMap.java
@@ -33,7 +33,7 @@
  * The cursor data is accessed by row key and column name via getValue().
  */
 public class ContentQueryMap extends Observable {
-    private Cursor mCursor;
+    private volatile Cursor mCursor;
     private String[] mColumnNames;
     private int mKeyColumn;
 
@@ -71,7 +71,7 @@
         // ContentProvider then read it once into the cache. Otherwise the cache will be filled 
         // automatically.
         if (!keepUpdated) {
-            readCursorIntoCache();
+            readCursorIntoCache(cursor);
         }
     }
 
@@ -128,27 +128,35 @@
 
     /** Requeries the cursor and reads the contents into the cache */
     public void requery() {
-        mDirty = false;
-        if (!mCursor.requery()) {
-            throw new IllegalStateException("trying to requery an already closed cursor");
+        final Cursor cursor = mCursor;
+        if (cursor == null) {
+            // If mCursor is null then it means there was a requery() in flight
+            // while another thread called close(), which nulls out mCursor.
+            // If this happens ignore the requery() since we are closed anyways.
+            return;
         }
-        readCursorIntoCache();
+        mDirty = false;
+        if (!cursor.requery()) {
+            // again, don't do anything if the cursor is already closed
+            return;
+        }
+        readCursorIntoCache(cursor);
         setChanged();
         notifyObservers();
     }
 
-    private synchronized void readCursorIntoCache() {
+    private synchronized void readCursorIntoCache(Cursor cursor) {
         // Make a new map so old values returned by getRows() are undisturbed.
         int capacity = mValues != null ? mValues.size() : 0;
         mValues = new HashMap<String, ContentValues>(capacity);
-        while (mCursor.moveToNext()) {
+        while (cursor.moveToNext()) {
             ContentValues values = new ContentValues();
             for (int i = 0; i < mColumnNames.length; i++) {
                 if (i != mKeyColumn) {
-                    values.put(mColumnNames[i], mCursor.getString(i));
+                    values.put(mColumnNames[i], cursor.getString(i));
                 }
             }
-            mValues.put(mCursor.getString(mKeyColumn), values);
+            mValues.put(cursor.getString(mKeyColumn), values);
         }
     }