Merge "Guard against NPE in equals checks." into nyc-dev
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 4233b36..0ae2a5c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -536,7 +536,11 @@
         @Override
         public void onItemStateChanged(String modelId, boolean selected) {
             final Cursor cursor = mModel.getItem(modelId);
-            checkNotNull(cursor, "Cursor cannot be null.");
+            if (cursor == null) {
+                Log.e(TAG, "Model returned null cursor for document: " + modelId
+                        + ". Ignoring state changed event.");
+                return;
+            }
 
             // TODO: Should this be happening in onSelectionChanged? Technically this callback is
             // triggered on "silent" selection updates (i.e. we might be reacting to unfinalized
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index 4b5499a..1c696ad 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -39,6 +39,7 @@
 import java.io.IOException;
 import java.net.ProtocolException;
 import java.text.Collator;
+import java.util.Objects;
 
 /**
  * Representation of a {@link Document}.
@@ -263,16 +264,23 @@
         return derivedUri.hashCode() + mimeType.hashCode();
     }
 
-    public boolean equals(Object other) {
-        if (this == other) {
-            return true;
-        } else if (!(other instanceof DocumentInfo)) {
+    public boolean equals(Object o) {
+        if (o == null) {
             return false;
         }
 
-        DocumentInfo that = (DocumentInfo) other;
-        // Uri + mime type should be totally unique.
-        return derivedUri.equals(that.derivedUri) && mimeType.equals(that.mimeType);
+        if (this == o) {
+            return true;
+        }
+
+        if (o instanceof DocumentInfo) {
+            DocumentInfo other = (DocumentInfo) o;
+            // Uri + mime type should be totally unique.
+            return Objects.equals(derivedUri, other.derivedUri)
+                    && Objects.equals(mimeType, other.mimeType);
+        }
+
+        return false;
     }
 
     public static String getCursorString(Cursor cursor, String columnName) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index ffa8f59..f4a97be 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -276,12 +276,21 @@
 
     @Override
     public boolean equals(Object o) {
-        if (o instanceof RootInfo) {
-            final RootInfo root = (RootInfo) o;
-            return Objects.equals(authority, root.authority) && Objects.equals(rootId, root.rootId);
-        } else {
+        if (o == null) {
             return false;
         }
+
+        if (this == o) {
+            return true;
+        }
+
+        if (o instanceof RootInfo) {
+            RootInfo other = (RootInfo) o;
+            return Objects.equals(authority, other.authority)
+                    && Objects.equals(rootId, other.rootId);
+        }
+
+        return false;
     }
 
     @Override
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StateTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StateTest.java
index b74b985..f057850 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StateTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StateTest.java
@@ -23,18 +23,44 @@
 
 @SmallTest
 public class StateTest extends AndroidTestCase {
-    public void testPushDocument() {
-        final State state = new State();
-        final DocumentInfo infoFirst = new DocumentInfo();
-        infoFirst.displayName = "firstDirectory";
-        final DocumentInfo infoSecond = new DocumentInfo();
-        infoSecond.displayName = "secondDirectory";
-        assertFalse(state.hasLocationChanged());
-        state.pushDocument(infoFirst);
-        state.pushDocument(infoSecond);
-        assertTrue(state.hasLocationChanged());
-        assertEquals("secondDirectory", state.stack.getFirst().displayName);
-        state.popDocument();
-        assertEquals("firstDirectory", state.stack.getFirst().displayName);
+
+    private static final DocumentInfo DIR_1;
+    private static final DocumentInfo DIR_2;
+
+    private State mState;
+
+    static {
+        DIR_1 = new DocumentInfo();
+        DIR_1.displayName = "firstDirectory";
+        DIR_2 = new DocumentInfo();
+        DIR_2.displayName = "secondDirectory";
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mState = new State();
+    }
+
+    public void testInitialStateEmpty() {
+        assertFalse(mState.hasLocationChanged());
+    }
+
+    public void testPushDocument_ChangesLocation() {
+        mState.pushDocument(DIR_1);
+        mState.pushDocument(DIR_2);
+        assertTrue(mState.hasLocationChanged());
+    }
+
+    public void testPushDocument_ModifiesStack() {
+        mState.pushDocument(DIR_1);
+        mState.pushDocument(DIR_2);
+        assertEquals(DIR_2, mState.stack.getFirst());
+    }
+
+    public void testPopDocument_ModifiesStack() {
+        mState.pushDocument(DIR_1);
+        mState.pushDocument(DIR_2);
+        mState.popDocument();
+        assertEquals(DIR_1, mState.stack.getFirst());
     }
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java
index a6aba7b..2481dc3 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java
@@ -22,30 +22,36 @@
 @SmallTest
 public class DocumentInfoTest extends AndroidTestCase {
 
+    private static final DocumentInfo TEST_DOC
+            = createDocInfo("authority.a", "doc.1", "text/plain");
+
     public void testEquals() throws Exception {
-        DocumentInfo doc = createDocInfo("authority.a", "doc.1", "text/plain");
-        assertEquals(doc, doc);
+        assertEquals(TEST_DOC, TEST_DOC);
+        assertEquals(TEST_DOC, createDocInfo("authority.a", "doc.1", "text/plain"));
+    }
+
+    public void testEquals_HandlesNulls() throws Exception {
+        assertFalse(TEST_DOC.equals(null));
+    }
+
+    public void testEquals_HandlesNullFields() throws Exception {
+        assertFalse(TEST_DOC.equals(new DocumentInfo()));
+        assertFalse(new DocumentInfo().equals(TEST_DOC));
     }
 
     public void testNotEquals_differentAuthority() throws Exception {
-        DocumentInfo docA = createDocInfo("authority.a", "doc.1", "text/plain");
-        DocumentInfo docB = createDocInfo("authority.b", "doc.1", "text/plain");
-        assertFalse(docA.equals(docB));
+        assertFalse(TEST_DOC.equals(createDocInfo("authority.b", "doc.1", "text/plain")));
     }
 
     public void testNotEquals_differentDocId() throws Exception {
-        DocumentInfo docA = createDocInfo("authority.a", "doc.1", "text/plain");
-        DocumentInfo docB = createDocInfo("authority.a", "doc.2", "text/plain");
-        assertFalse(docA.equals(docB));
+        assertFalse(TEST_DOC.equals(createDocInfo("authority.a", "doc.2", "text/plain")));
     }
 
     public void testNotEquals_differentMimetype() throws Exception {
-        DocumentInfo docA = createDocInfo("authority.a", "doc.1", "text/plain");
-        DocumentInfo docB = createDocInfo("authority.a", "doc.1", "image/png");
-        assertFalse(docA.equals(docB));
+        assertFalse(TEST_DOC.equals(createDocInfo("authority.a", "doc.1", "image/png")));
     }
 
-    private DocumentInfo createDocInfo(String authority, String docId, String mimeType) {
+    private static DocumentInfo createDocInfo(String authority, String docId, String mimeType) {
         DocumentInfo doc = new DocumentInfo();
         doc.authority = authority;
         doc.documentId = docId;