Fix crash when altering rate for voicemails without content.

- Repro for crash was to click on voicemail from call log without
  content, try to play it (which would silently fail), then try to
  use the rate increase/decrease buttons.
- The error being reported to the ui was disabling all the ui elements
  but not the rate buttons.
- This fix makes the ui disable the rate buttons too, as well as showing
  a Toast, and logging the exception to logcat.
- This cl also adds the unit tests that prove that the bug is fixed.
- In the process, I discovered another bug where missing extras from
  the intent used to start the CallDetailsActivity could cause a crash,
  so this adds a test and a fix for that case too.
- Also introduces IntegrationTestUtils class, with some useful methods
  for doing things like clicking on different parts of the ui.

Bug: 5047879
Change-Id: I46d18723fe783a7a820446e1e13e19b5af82fa5c
diff --git a/src/com/android/contacts/CallDetailActivityQueryHandler.java b/src/com/android/contacts/CallDetailActivityQueryHandler.java
index c1d87b2..df5b4f7 100644
--- a/src/com/android/contacts/CallDetailActivityQueryHandler.java
+++ b/src/com/android/contacts/CallDetailActivityQueryHandler.java
@@ -57,8 +57,8 @@
      * If the voicemail record does not have an audio yet then it fires the second query to get the
      * voicemail status of the associated source.
      */
-    public void startVoicemailStatusQuery(Uri voicemaiUri) {
-        startQuery(QUERY_VOICEMAIL_CONTENT_TOKEN, null, voicemaiUri, VOICEMAIL_CONTENT_PROJECTION,
+    public void startVoicemailStatusQuery(Uri voicemailUri) {
+        startQuery(QUERY_VOICEMAIL_CONTENT_TOKEN, null, voicemailUri, VOICEMAIL_CONTENT_PROJECTION,
                 null, null, null);
     }
 
@@ -67,11 +67,12 @@
         try {
             if (token == QUERY_VOICEMAIL_CONTENT_TOKEN) {
                 // Query voicemail status only if this voicemail record does not have audio.
-                if (cursor.moveToFirst() && hasNoAudio(cursor)) {
+                if (moveToFirst(cursor) && hasNoAudio(cursor)) {
                     startQuery(QUERY_VOICEMAIL_STATUS_TOKEN, null,
                             Status.buildSourceUri(getSourcePackage(cursor)),
                             VoicemailStatusHelperImpl.PROJECTION, null, null, null);
                 } else {
+                    // nothing to show in status
                     mCallDetailActivity.updateVoicemailStatusMessage(null);
                 }
             } else if (token == QUERY_VOICEMAIL_STATUS_TOKEN) {
@@ -84,6 +85,15 @@
         }
     }
 
+    /** Check that the cursor is non-null and can be moved to first. */
+    private boolean moveToFirst(Cursor cursor) {
+        if (cursor == null || !cursor.moveToFirst()) {
+            Log.e(TAG, "Cursor not valid, could not move to first");
+            return false;
+        }
+        return true;
+    }
+
     private boolean hasNoAudio(Cursor voicemailCursor) {
         return voicemailCursor.getInt(HAS_CONTENT_COLUMN_INDEX) == 0;
     }