Added a field to track if the call is to a voicemail instance.

Refactored common code between CallerInfo and CallerInfoAsyncQuery that deal
with voicemail number comparison.

In CallerInfo.java added a new field mIsVoiceMail to indicate this is a
voicemail call.
Added a new method to convert the CallerInfo into a VM instance.
Added a new method to generate a debug string from an instance.

PhoneNumberUtils has a new method "isVoiceMailNumber" to check if a number
is a VM one. I left the method as hidden. Previously any security exception
failure was cached in a static variable. I removed that and
privilege the optmistic scenario. I am not sure if the security exception
is only for the 'regular' telephony layer and if it applies if a 3rd party
VM app is installed (e.g googlevoice), hence i removed the cashing to make
sure we can pick up new voicemail providers when installed/enabled/disabled.

Bug:2112640
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 4368464..2672c6d 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -129,6 +129,8 @@
             return uri.getSchemeSpecificPart();
         }
 
+        // TODO: We don't check for SecurityException here (requires
+        // READ_PHONE_STATE permission).
         if (scheme.equals("voicemail")) {
             return TelephonyManager.getDefault().getVoiceMailNumber();
         }
@@ -1179,6 +1181,35 @@
     }
 
     /**
+     * isVoiceMailNumber: checks a given number against the voicemail
+     *   number provided by the RIL and SIM card. The caller must have
+     *   the READ_PHONE_STATE credential.
+     *
+     * @param number the number to look up.
+     * @return true if the number is in the list of voicemail. False
+     * otherwise, including if the caller does not have the permission
+     * to read the VM number.
+     * @hide TODO: pending API Council approval
+     */
+    public static boolean isVoiceMailNumber(String number) {
+        String vmNumber;
+
+        try {
+            vmNumber = TelephonyManager.getDefault().getVoiceMailNumber();
+        } catch (SecurityException ex) {
+            return false;
+        }
+
+        // Strip the separators from the number before comparing it
+        // to the list.
+        number = extractNetworkPortion(number);
+
+        // compare tolerates null so we need to make sure that we
+        // don't return true when both are null.
+        return !TextUtils.isEmpty(number) && compare(number, vmNumber);
+    }
+
+    /**
      * Translates any alphabetic letters (i.e. [A-Za-z]) in the
      * specified phone number into the equivalent numeric digits,
      * according to the phone keypad letter mapping described in
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index c8490e9..01b1746 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -101,13 +101,12 @@
     public boolean isCachedPhotoCurrent;
 
     private boolean mIsEmergency;
-
-    // Don't keep checking VM if it's going to throw an exception for this proc.
-    private static boolean sSkipVmCheck = false;
+    private boolean mIsVoiceMail;
 
     public CallerInfo() {
         // TODO: Move all the basic initialization here?
         mIsEmergency = false;
+        mIsVoiceMail = false;
     }
 
     /**
@@ -220,32 +219,15 @@
     public static CallerInfo getCallerInfo(Context context, String number) {
         if (TextUtils.isEmpty(number)) {
             return null;
-        } else {
-            // Change the callerInfo number ONLY if it is an emergency number
-            // or if it is the voicemail number.  If it is either, take a
-            // shortcut and skip the query.
-            if (PhoneNumberUtils.isEmergencyNumber(number)) {
-                return new CallerInfo().markAsEmergency(context);
-            } else {
-                try {
-                    if (!sSkipVmCheck && PhoneNumberUtils.compare(number,
-                                TelephonyManager.getDefault().getVoiceMailNumber())) {
-                        CallerInfo ci = new CallerInfo();
+        }
 
-                        // Note we're setting the phone number here (refer to javadoc
-                        // comments at the top of CallerInfo class).
-                        ci.phoneNumber = TelephonyManager.getDefault().getVoiceMailAlphaTag();
-                        // TODO: FIND ANOTHER ICON
-                        //info.photoResource = android.R.drawable.badge_voicemail;
-                        return ci;
-                    }
-                } catch (SecurityException ex) {
-                    // Don't crash if this process doesn't have permission to
-                    // retrieve VM number.  It's still allowed to look up caller info.
-                    // But don't try it again.
-                    sSkipVmCheck = true;
-                }
-            }
+        // Change the callerInfo number ONLY if it is an emergency number
+        // or if it is the voicemail number.  If it is either, take a
+        // shortcut and skip the query.
+        if (PhoneNumberUtils.isEmergencyNumber(number)) {
+            return new CallerInfo().markAsEmergency(context);
+        } else if (PhoneNumberUtils.isVoiceMailNumber(number)) {
+            return new CallerInfo().markAsVoiceMail();
         }
 
         Uri contactUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
@@ -304,6 +286,13 @@
     }
 
     /**
+     * @return true if the caller info is a voicemail number.
+     */
+    public boolean isVoiceMailNumber() {
+        return mIsVoiceMail;
+    }
+
+    /**
      * Mark this CallerInfo as an emergency call.
      * @param context To lookup the localized 'Emergency Number' string.
      * @return this instance.
@@ -323,6 +312,37 @@
         return this;
     }
 
+
+    /**
+     * Mark this CallerInfo as a voicemail call. The voicemail label
+     * is obtained from the telephony manager. Caller must hold the
+     * READ_PHONE_STATE permission otherwise the phoneNumber will be
+     * set to null.
+     * @return this instance.
+     */
+    // TODO: As in the emergency number handling, we end up writing a
+    // string in the phone number field.
+    /* package */ CallerInfo markAsVoiceMail() {
+        mIsVoiceMail = true;
+
+        try {
+            String voiceMailLabel = TelephonyManager.getDefault().getVoiceMailAlphaTag();
+
+            phoneNumber = voiceMailLabel;
+        } catch (SecurityException se) {
+            // Should never happen: if this process does not have
+            // permission to retrieve VM tag, it should not have
+            // permission to retrieve VM number and would not call
+            // this method.
+            // Leave phoneNumber untouched.
+            Log.e(TAG, "Cannot access VoiceMail.", se);
+        }
+        // TODO: There is no voicemail picture?
+        // FIXME: FIND ANOTHER ICON
+        // photoResource = android.R.drawable.badge_voicemail;
+        return this;
+    }
+
     private static String normalize(String s) {
         if (s == null || s.length() > 0) {
             return s;
@@ -330,4 +350,31 @@
             return null;
         }
     }
+
+    /**
+     * @return a string debug representation of this instance.
+     */
+    public String toString() {
+        return new StringBuilder(384)
+                .append("\nname: " + name)
+                .append("\nphoneNumber: " + phoneNumber)
+                .append("\ncnapName: " + cnapName)
+                .append("\nnumberPresentation: " + numberPresentation)
+                .append("\nnamePresentation: " + namePresentation)
+                .append("\ncontactExits: " + contactExists)
+                .append("\nphoneLabel: " + phoneLabel)
+                .append("\nnumberType: " + numberType)
+                .append("\nnumberLabel: " + numberLabel)
+                .append("\nphotoResource: " + photoResource)
+                .append("\nperson_id: " + person_id)
+                .append("\nneedUpdate: " + needUpdate)
+                .append("\ncontactRefUri: " + contactRefUri)
+                .append("\ncontactRingtoneUri: " + contactRefUri)
+                .append("\nshouldSendToVoicemail: " + shouldSendToVoicemail)
+                .append("\ncachedPhoto: " + cachedPhoto)
+                .append("\nisCachedPhotoCurrent: " + isCachedPhotoCurrent)
+                .append("\nemergency: " + mIsEmergency)
+                .append("\nvoicemail " + mIsVoiceMail)
+                .toString();
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index 4227a84..802e79b 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -47,9 +47,6 @@
 
     private CallerInfoAsyncQueryHandler mHandler;
 
-    // Don't keep checking VM if it's going to throw an exception for this proc.
-    private static boolean sSkipVmCheck = false;
-
     /**
      * Interface for a CallerInfoAsyncQueryHandler result return.
      */
@@ -227,18 +224,7 @@
                     // comments at the top of CallerInfo class).
                     mCallerInfo = new CallerInfo().markAsEmergency(mQueryContext);
                 } else if (cw.event == EVENT_VOICEMAIL_NUMBER) {
-                    mCallerInfo = new CallerInfo();
-                    try {
-                        // Note we're setting the phone number here (refer to javadoc
-                        // comments at the top of CallerInfo class).
-                        mCallerInfo.phoneNumber =
-                                TelephonyManager.getDefault().getVoiceMailAlphaTag();
-                    } catch (SecurityException ex) {
-                        // Should never happen: if this process does not have
-                        // permission to retrieve VM tag, it should not have
-                        // permission to retrieve VM number and would not generate
-                        // an EVENT_VOICEMAIL_NUMBER.  But if it happens, don't crash.
-                    }
+                    mCallerInfo = new CallerInfo().markAsVoiceMail();
                 } else {
                     mCallerInfo = CallerInfo.getCallerInfo(mQueryContext, mQueryUri, cursor);
                     // Use the number entered by the user for display.
@@ -258,7 +244,7 @@
             //notify the listener that the query is complete.
             if (cw.listener != null) {
                 if (DBG) log("notifying listener: " + cw.listener.getClass().toString() +
-                        " for token: " + token);
+                             " for token: " + token + mCallerInfo);
                 cw.listener.onQueryComplete(token, cw.cookie, mCallerInfo);
             }
         }
@@ -315,23 +301,10 @@
         // check to see if these are recognized numbers, and use shortcuts if we can.
         if (PhoneNumberUtils.isEmergencyNumber(number)) {
             cw.event = EVENT_EMERGENCY_NUMBER;
+        } else if (PhoneNumberUtils.isVoiceMailNumber(number)) {
+            cw.event = EVENT_VOICEMAIL_NUMBER;
         } else {
-            String vmNumber = null;
-            if (!sSkipVmCheck){
-                try {
-                    vmNumber = TelephonyManager.getDefault().getVoiceMailNumber();
-                } catch (SecurityException ex) {
-                    // Don't crash if this process doesn't have permission to
-                    // retrieve VM number.  It's still allowed to look up caller info.
-                    // But don't try it again.
-                    sSkipVmCheck = true;
-                }
-            }
-            if (PhoneNumberUtils.compare(number, vmNumber)) {
-                cw.event = EVENT_VOICEMAIL_NUMBER;
-            } else {
-                cw.event = EVENT_NEW_QUERY;
-            }
+            cw.event = EVENT_NEW_QUERY;
         }
 
         c.mHandler.startQuery (token, cw, contactRef, null, null, null, null);
diff --git a/telephony/tests/TelephonyTest/AndroidManifest.xml b/telephony/tests/TelephonyTest/AndroidManifest.xml
index c0cc0d5..b2a481b 100644
--- a/telephony/tests/TelephonyTest/AndroidManifest.xml
+++ b/telephony/tests/TelephonyTest/AndroidManifest.xml
@@ -28,8 +28,9 @@
             </intent-filter>
         </activity>
     </application>
-     <instrumentation android:name=".TelephonyUnitTestRunner"
-         android:targetPackage="com.android.telephonytest"
-         android:label="Telephony unit tests InstrumentationRunner">
-     </instrumentation>
+    <instrumentation android:name=".TelephonyUnitTestRunner"
+        android:targetPackage="com.android.telephonytest"
+        android:label="Telephony unit tests InstrumentationRunner">
+    </instrumentation>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
 </manifest>
diff --git a/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java b/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java
index 5da940d..9e1af31 100644
--- a/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java
+++ b/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java
@@ -37,6 +37,7 @@
     public TestSuite getAllTests() {
         TestSuite suite = new InstrumentationTestSuite(this);
         suite.addTestSuite(com.android.telephonytest.unit.CallerInfoUnitTest.class);
+        suite.addTestSuite(com.android.telephonytest.unit.PhoneNumberUtilsUnitTest.class);
         return suite;
     }
 
diff --git a/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/CallerInfoUnitTest.java b/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/CallerInfoUnitTest.java
index 4cd0266..0f24f15 100644
--- a/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/CallerInfoUnitTest.java
+++ b/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/CallerInfoUnitTest.java
@@ -107,6 +107,16 @@
         assertIsValidEmergencyCallerInfo();
     }
 
+    // TODO: Add more tests:
+    /**
+     * Check if the voice mail number cannot be retrieved that the
+     * original phone number is preserved.
+     */
+    /**
+     * Check the markAs* methods work.
+     */
+
+
     //
     // Helpers
     //
diff --git a/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java b/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java
new file mode 100644
index 0000000..2d3c548
--- /dev/null
+++ b/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telephonytest.unit;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+
+/*
+ * Check the PhoneNumberUtils utility class works as expected.
+ *
+ */
+
+public class PhoneNumberUtilsUnitTest extends AndroidTestCase {
+    private String mVoiceMailNumber;
+    private static final String TAG = "PhoneNumberUtilsUnitTest";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // FIXME: Why are we getting a security exception here? The
+        // permission is declared in the manifest....
+        // mVoiceMailNumber = TelephonyManager.getDefault().getVoiceMailNumber();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * Basic checks for the VoiceMail number.
+     * Assumes READ_PHONE_STATE permission and we don't have it.
+     */
+    // TODO: Figure out why we don't have the permission declared in the manifest.
+    @SmallTest
+    public void testWithNumberNotEqualToVoiceMail() throws Exception {
+        assertFalse(PhoneNumberUtils.isVoiceMailNumber("911"));
+        assertFalse(PhoneNumberUtils.isVoiceMailNumber("tel:911"));
+        assertFalse(PhoneNumberUtils.isVoiceMailNumber("+18001234567"));
+        assertFalse(PhoneNumberUtils.isVoiceMailNumber(""));
+        assertFalse(PhoneNumberUtils.isVoiceMailNumber(null));
+        // FIXME:
+        // assertTrue(PhoneNumberUtils.isVoiceMailNumber(mVoiceMailNumber));
+    }
+
+}