New field in CallerInfo to cache if the call is an emergency one.

Fixed a bug in the sync call, the photo resource was not set to picture_emergency.

Added tests to check the correct emergency fields are set in the caller info instance.

Bug 2112640
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index e1bd1db..c8490e9 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -100,10 +100,14 @@
     public Drawable cachedPhoto;
     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;
 
     public CallerInfo() {
+        // TODO: Move all the basic initialization here?
+        mIsEmergency = false;
     }
 
     /**
@@ -221,13 +225,7 @@
             // or if it is the voicemail number.  If it is either, take a
             // shortcut and skip the query.
             if (PhoneNumberUtils.isEmergencyNumber(number)) {
-                CallerInfo ci = new CallerInfo();
-
-                // Note we're setting the phone number here (refer to javadoc
-                // comments at the top of CallerInfo class).
-                ci.phoneNumber = context.getString(
-                        com.android.internal.R.string.emergency_call_dialog_number_for_display);
-                return ci;
+                return new CallerInfo().markAsEmergency(context);
             } else {
                 try {
                     if (!sSkipVmCheck && PhoneNumberUtils.compare(number,
@@ -296,6 +294,35 @@
         return callerID;
     }
 
+    // Accessors
+
+    /**
+     * @return true if the caller info is an emergency number.
+     */
+    public boolean isEmergencyNumber() {
+        return mIsEmergency;
+    }
+
+    /**
+     * Mark this CallerInfo as an emergency call.
+     * @param context To lookup the localized 'Emergency Number' string.
+     * @return this instance.
+     */
+    // TODO: Note we're setting the phone number here (refer to
+    // javadoc comments at the top of CallerInfo class) to a localized
+    // string 'Emergency Number'. This is pretty bad because we are
+    // making UI work here instead of just packaging the data. We
+    // should set the phone number to the dialed number and name to
+    // 'Emergency Number' and let the UI make the decision about what
+    // should be displayed.
+    /* package */ CallerInfo markAsEmergency(Context context) {
+        phoneNumber = context.getString(
+            com.android.internal.R.string.emergency_call_dialog_number_for_display);
+        photoResource = com.android.internal.R.drawable.picture_emergency;
+        mIsEmergency = true;
+        return this;
+    }
+
     private static String normalize(String s) {
         if (s == null || s.length() > 0) {
             return s;
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index 3d4f78c..4227a84 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -37,7 +37,7 @@
 public class CallerInfoAsyncQuery {
 
     private static final boolean DBG = false;
-    private static final String LOG_TAG = "PHONE";
+    private static final String LOG_TAG = "CallerInfoAsyncQuery";
 
     private static final int EVENT_NEW_QUERY = 1;
     private static final int EVENT_ADD_LISTENER = 2;
@@ -223,13 +223,9 @@
                 // voicemail number, and adjust other data (including photoResource)
                 // accordingly.
                 if (cw.event == EVENT_EMERGENCY_NUMBER) {
-                    mCallerInfo = new CallerInfo();
                     // Note we're setting the phone number here (refer to javadoc
                     // comments at the top of CallerInfo class).
-                    mCallerInfo.phoneNumber = mQueryContext.getString(com.android.internal
-                            .R.string.emergency_call_dialog_number_for_display);
-                    mCallerInfo.photoResource = com.android.internal.R.drawable.picture_emergency;
-
+                    mCallerInfo = new CallerInfo().markAsEmergency(mQueryContext);
                 } else if (cw.event == EVENT_VOICEMAIL_NUMBER) {
                     mCallerInfo = new CallerInfo();
                     try {
@@ -390,4 +386,3 @@
         Log.d(LOG_TAG, msg);
     }
 }
-
diff --git a/telephony/tests/TelephonyTest/Android.mk b/telephony/tests/TelephonyTest/Android.mk
new file mode 100644
index 0000000..1ef8448
--- /dev/null
+++ b/telephony/tests/TelephonyTest/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_PACKAGE_NAME := telephonytest
+
+include $(BUILD_PACKAGE)
diff --git a/telephony/tests/TelephonyTest/AndroidManifest.xml b/telephony/tests/TelephonyTest/AndroidManifest.xml
new file mode 100644
index 0000000..c0cc0d5
--- /dev/null
+++ b/telephony/tests/TelephonyTest/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.telephonytest">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:label="TelephonyTest"
+                android:name="TelephonyTest">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+     <instrumentation android:name=".TelephonyUnitTestRunner"
+         android:targetPackage="com.android.telephonytest"
+         android:label="Telephony unit tests InstrumentationRunner">
+     </instrumentation>
+</manifest>
diff --git a/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java b/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java
new file mode 100644
index 0000000..5da940d
--- /dev/null
+++ b/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+import junit.framework.TestSuite;
+
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+
+/**
+ * Instrumentation Test Runner for all Telephony unit tests.
+ *
+ * Running all tests:
+ *
+ *   runtest telephony-unit
+ * or
+ *   adb shell am instrument -w com.android.telephonytest/.TelephonyUnitTestRunner
+ */
+
+public class TelephonyUnitTestRunner extends InstrumentationTestRunner {
+
+    @Override
+    public TestSuite getAllTests() {
+        TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(com.android.telephonytest.unit.CallerInfoUnitTest.class);
+        return suite;
+    }
+
+    @Override
+    public ClassLoader getLoader() {
+        return TelephonyUnitTestRunner.class.getClassLoader();
+    }
+}
diff --git a/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/CallerInfoUnitTest.java b/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/CallerInfoUnitTest.java
new file mode 100644
index 0000000..4cd0266
--- /dev/null
+++ b/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/CallerInfoUnitTest.java
@@ -0,0 +1,230 @@
+/*
+ * 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.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import com.android.internal.telephony.CallerInfo;
+import com.android.internal.telephony.CallerInfoAsyncQuery;
+import android.util.Log;
+import android.os.Looper;
+import android.test.ActivityInstrumentationTestCase;
+import android.util.StringBuilderPrinter;
+
+/*
+ * Check the CallerInfo utility class works as expected.
+ *
+ */
+
+public class CallerInfoUnitTest extends AndroidTestCase {
+    private CallerInfo mInfo;
+    private Context mContext;
+
+    private static final String kEmergencyNumber = "Emergency Number";
+    private static final int kToken = 0xdeadbeef;
+    private static final String TAG = "CallerInfoUnitTest";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = new MockContext();
+        mInfo = new CallerInfo();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * Checks the caller info instance is flagged as an emergency if
+     * the number is an emergency one. There is no test for the
+     * contact based constructors because emergency number are not in
+     * the contact DB.
+     */
+    @SmallTest
+    public void testEmergencyIsProperlySet() throws Exception {
+        assertFalse(mInfo.isEmergencyNumber());
+
+        mInfo = CallerInfo.getCallerInfo(mContext, "911");
+        assertIsValidEmergencyCallerInfo();
+
+        mInfo = CallerInfo.getCallerInfo(mContext, "tel:911");
+        assertIsValidEmergencyCallerInfo();
+
+
+        // This one hits the content resolver.
+        mInfo = CallerInfo.getCallerInfo(mContext, "18001234567");
+        assertFalse(mInfo.isEmergencyNumber());
+    }
+
+    /**
+     * Same as testEmergencyIsProperlySet but uses the async query api.
+     */
+    @SmallTest
+    public void testEmergencyIsProperlySetUsingAsyncQuery() throws Exception {
+        QueryRunner query;
+
+        query = new QueryRunner("911");
+        query.runAndCheckCompletion();
+        assertIsValidEmergencyCallerInfo();
+
+        query = new QueryRunner("tel:911");
+        query.runAndCheckCompletion();
+        assertIsValidEmergencyCallerInfo();
+
+        query = new QueryRunner("18001234567");
+        query.runAndCheckCompletion();
+        assertFalse(mInfo.isEmergencyNumber());
+    }
+
+    /**
+     * For emergency caller info, phoneNumber should be set to the
+     * string emergency_call_dialog_number_for_display and the
+     * photoResource should be set to the picture_emergency drawable.
+     */
+    @SmallTest
+    public void testEmergencyNumberAndPhotoAreSet() throws Exception {
+        mInfo = CallerInfo.getCallerInfo(mContext, "911");
+
+        assertIsValidEmergencyCallerInfo();
+    }
+
+    //
+    // Helpers
+    //
+
+    // Partial implementation of MockResources.
+    public class MockResources extends android.test.mock.MockResources
+    {
+        @Override
+        public String getString(int resId) throws Resources.NotFoundException {
+            switch (resId) {
+                case com.android.internal.R.string.emergency_call_dialog_number_for_display:
+                    return kEmergencyNumber;
+                default:
+                    throw new UnsupportedOperationException("Missing handling for resid " + resId);
+            }
+        }
+    }
+
+    // Partial implementation of MockContext.
+    public class MockContext extends android.test.mock.MockContext {
+        private ContentResolver mResolver;
+        private Resources mResources;
+
+        public MockContext() {
+            mResolver = new android.test.mock.MockContentResolver();
+            mResources = new MockResources();
+        }
+
+        @Override
+        public ContentResolver getContentResolver() {
+            return mResolver;
+        }
+
+        @Override
+        public Resources getResources() {
+            return mResources;
+        }
+    }
+
+    /**
+     * Class to run a CallerInfoAsyncQuery in a separate thread, with
+     * its own Looper. We cannot use the main Looper because on the
+     * 1st quit the thread is maked dead, ie no further test can use
+     * it. Also there is not way to inject a Looper instance in the
+     * query, so we have to use a thread with its own looper.
+     */
+    private class QueryRunner extends Thread
+            implements CallerInfoAsyncQuery.OnQueryCompleteListener {
+        private Looper mLooper;
+        private String mNumber;
+        private boolean mAsyncCompleted;
+
+        public QueryRunner(String number) {
+            super();
+            mNumber = number;
+        }
+
+        // Run the query in the thread, wait for completion.
+        public void runAndCheckCompletion() throws InterruptedException {
+            start();
+            join();
+            assertTrue(mAsyncCompleted);
+        }
+
+        @Override
+        public void run() {
+            Looper.prepare();
+            mLooper = Looper.myLooper();
+            mAsyncCompleted = false;
+            // The query will pick the thread local looper we've just prepared.
+            CallerInfoAsyncQuery.startQuery(kToken, mContext, mNumber, this, null);
+            mLooper.loop();
+        }
+
+        // Quit the Looper on the 1st callback
+        // (EVENT_EMERGENCY_NUMBER). There is another message
+        // (EVENT_END_OF_QUEUE) that will never be delivered because
+        // the test has exited. The corresponding stack trace
+        // "Handler{xxxxx} sending message to a Handler on a dead
+        // thread" can be ignored.
+        public void onQueryComplete(int token, Object cookie, CallerInfo info) {
+            mAsyncCompleted = true;
+            mInfo = info;
+            mLooper.quit();
+        }
+    }
+
+    /**
+     * Fail if mInfo does not contain a valid emergency CallerInfo instance.
+     */
+    private void assertIsValidEmergencyCallerInfo() throws Exception {
+        assertTrue(mInfo.isEmergencyNumber());
+
+        // For emergency caller info, phoneNumber should be set to the
+        // string emergency_call_dialog_number_for_display and the
+        // photoResource should be set to the picture_emergency drawable.
+        assertEquals(kEmergencyNumber, mInfo.phoneNumber);
+        assertEquals(com.android.internal.R.drawable.picture_emergency, mInfo.photoResource);
+
+        // The name should be null
+        assertNull(mInfo.name);
+        assertEquals(0, mInfo.namePresentation);
+        assertNull(mInfo.cnapName);
+        assertEquals(0, mInfo.numberPresentation);
+
+        assertFalse(mInfo.contactExists);
+        assertEquals(0, mInfo.person_id);
+        assertFalse(mInfo.needUpdate);
+        assertNull(mInfo.contactRefUri);
+
+        assertNull(mInfo.phoneLabel);
+        assertEquals(0, mInfo.numberType);
+        assertNull(mInfo.numberLabel);
+
+        assertNull(mInfo.contactRingtoneUri);
+        assertFalse(mInfo.shouldSendToVoicemail);
+
+        assertNull(mInfo.cachedPhoto);
+        assertFalse(mInfo.isCachedPhotoCurrent);
+    }
+}