E911 call fix in ECM

Based on the VZW requirement, phone should be still in ECM mode in 2nd emergency call.
but in the current phone call, if a 2nd emergency call is originated, ECM mode will exit.

For fixing this problem, the coding design is as below:
1. In framework, canceling the first ECM timer immediately upon the origination of the
   2nd E911 call, and restarting a new timer when the 2nd E911 ends.
2. Framework needs to syncronize the timer with phone app by sending notification to phone app to
   inform timer is canceled or re-started, since phone app needs to show how much ECM time left
   on the status bar.
3. In phone app's emergency callback mode service, the timer in this service will be canceled
   when it receives the timer cancel notification from framework; the timer will be restarted
   once it receives timer restart notification from framework.
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 622b47d..3f9744f 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -434,6 +434,20 @@
     void unregisterForMmiComplete(Handler h);
 
     /**
+     * Registration point for Ecm timer reset
+     * @param h handler to notify
+     * @param what user-defined message code
+     * @param obj placed in Message.obj
+     */
+    public void registerForEcmTimerReset(Handler h, int what, Object obj);
+
+    /**
+     * Unregister for notification for Ecm timer reset
+     * @param h Handler to be removed from the registrant list.
+     */
+    public void unregisterForEcmTimerReset(Handler h);
+
+    /**
      * Returns a list of MMI codes that are pending. (They have initiated
      * but have not yet completed).
      * Presently there is only ever one.
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 6f4aef9..04a3749 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -760,6 +760,16 @@
         Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
     }
 
+    public void registerForEcmTimerReset(Handler h, int what, Object obj) {
+        // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+    }
+
+    public void unregisterForEcmTimerReset(Handler h) {
+        // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+        Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+    }
+
     public void registerForSignalInfo(Handler h, int what, Object obj) {
         mCM.registerForSignalInfo(h, what, obj);
     }
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index f2568c1..8683278 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -323,6 +323,14 @@
         mActivePhone.unregisterForSubscriptionInfoReady(h);
     }
 
+    public void registerForEcmTimerReset(Handler h, int what, Object obj) {
+        mActivePhone.registerForEcmTimerReset(h,what,obj);
+    }
+
+    public void unregisterForEcmTimerReset(Handler h) {
+        mActivePhone.unregisterForEcmTimerReset(h);
+    }
+
     public boolean getIccRecordsLoaded() {
         return mActivePhone.getIccRecordsLoaded();
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index f0c0ea2..237d533 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -28,6 +28,8 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.os.RemoteException;
@@ -72,8 +74,7 @@
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
 
 import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
+
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -87,10 +88,14 @@
 
     // Default Emergency Callback Mode exit timer
     private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000;
+
     static final String VM_COUNT_CDMA = "vm_count_key_cdma";
     private static final String VM_NUMBER_CDMA = "vm_number_key_cdma";
     private String mVmNumber = null;
 
+    static final int RESTART_ECM_TIMER = 0; // restart Ecm timer
+    static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer
+
     //***** Instance Variables
     CdmaCallTracker mCT;
     CdmaSMSDispatcher mSMS;
@@ -103,6 +108,7 @@
     RuimSmsInterfaceManager mRuimSmsInterfaceManager;
     PhoneSubInfo mSubInfo;
     EriManager mEriManager;
+    WakeLock mWakeLock;
 
     // mNvLoadedRegistrants are informed after the EVENT_NV_READY
     private RegistrantList mNvLoadedRegistrants = new RegistrantList();
@@ -110,17 +116,20 @@
     // mEriFileLoadedRegistrants are informed after the ERI text has been loaded
     private RegistrantList mEriFileLoadedRegistrants = new RegistrantList();
 
-    // mECMExitRespRegistrant is informed after the phone has been exited
+    // mEcmTimerResetRegistrants are informed after Ecm timer is canceled or re-started
+    private RegistrantList mEcmTimerResetRegistrants = new RegistrantList();
+
+    // mEcmExitRespRegistrant is informed after the phone has been exited
     //the emergency callback mode
     //keep track of if phone is in emergency callback mode
-    private boolean mIsPhoneInECMState;
-    private Registrant mECMExitRespRegistrant;
+    private boolean mIsPhoneInEcmState;
+    private Registrant mEcmExitRespRegistrant;
     private String mEsn;
     private String mMeid;
     // string to define how the carrier specifies its own ota sp number
     private String mCarrierOtaSpNumSchema;
 
-    // A runnable which is used to automatically exit from ECM after a period of time.
+    // A runnable which is used to automatically exit from Ecm after a period of time.
     private Runnable mExitEcmRunnable = new Runnable() {
         public void run() {
             exitEmergencyCallbackMode();
@@ -165,6 +174,9 @@
         mCM.registerForNVReady(h, EVENT_NV_READY, null);
         mCM.setEmergencyCallbackMode(h, EVENT_EMERGENCY_CALLBACK_MODE_ENTER, null);
 
+        PowerManager pm
+            = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,LOG_TAG);
 
         //Change the system setting
         SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE,
@@ -172,7 +184,7 @@
 
         // This is needed to handle phone process crashes
         String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
-        mIsPhoneInECMState = inEcm.equals("true");
+        mIsPhoneInEcmState = inEcm.equals("true");
 
         // get the string that specifies the carrier OTA Sp number
         mCarrierOtaSpNumSchema = SystemProperties.get(
@@ -244,6 +256,10 @@
 
     protected void finalize() {
         if(DBG) Log.d(LOG_TAG, "CDMAPhone finalized");
+        if (mWakeLock.isHeld()) {
+            Log.e(LOG_TAG, "UNEXPECTED; mWakeLock is held when finalizing.");
+            mWakeLock.release();
+        }
     }
 
 
@@ -525,11 +541,11 @@
     }
 
     public void setOnEcbModeExitResponse(Handler h, int what, Object obj) {
-        mECMExitRespRegistrant = new Registrant (h, what, obj);
+        mEcmExitRespRegistrant = new Registrant (h, what, obj);
     }
 
     public void unsetOnEcbModeExitResponse(Handler h) {
-        mECMExitRespRegistrant.clear();
+        mEcmExitRespRegistrant.clear();
     }
 
     public void registerForCallWaiting(Handler h, int what, Object obj) {
@@ -729,7 +745,7 @@
     public boolean enableDataConnectivity() {
 
         // block data activities when phone is in emergency callback mode
-        if (mIsPhoneInECMState) {
+        if (mIsPhoneInEcmState) {
             Intent intent = new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS);
             ActivityManagerNative.broadcastStickyIntent(intent, null);
             return false;
@@ -826,8 +842,9 @@
     void sendEmergencyCallbackModeChange(){
         //Send an Intent
         Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
-        intent.putExtra(PHONE_IN_ECM_STATE, mIsPhoneInECMState);
+        intent.putExtra(PHONE_IN_ECM_STATE, mIsPhoneInEcmState);
         ActivityManagerNative.broadcastStickyIntent(intent,null);
+        if (DBG) Log.d(LOG_TAG, "sendEmergencyCallbackModeChange");
     }
 
     /*package*/ void
@@ -859,15 +876,21 @@
 
     @Override
     public void exitEmergencyCallbackMode() {
+        if (mWakeLock.isHeld()) {
+            mWakeLock.release();
+        }
         // Send a message which will invoke handleExitEmergencyCallbackMode
         mCM.exitEmergencyCallbackMode(h.obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE));
     }
 
     private void handleEnterEmergencyCallbackMode(Message msg) {
-        Log.d(LOG_TAG, "Event EVENT_EMERGENCY_CALLBACK_MODE Received");
-        // if phone is not in ECM mode, and it's changed to ECM mode
-        if (mIsPhoneInECMState == false) {
-            mIsPhoneInECMState = true;
+        if (DBG) {
+            Log.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= "
+                    + mIsPhoneInEcmState);
+        }
+        // if phone is not in Ecm mode, and it's changed to Ecm mode
+        if (mIsPhoneInEcmState == false) {
+            mIsPhoneInEcmState = true;
             // notify change
             sendEmergencyCallbackModeChange();
             setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "true");
@@ -877,23 +900,27 @@
             long delayInMillis = SystemProperties.getLong(
                     TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE);
             h.postDelayed(mExitEcmRunnable, delayInMillis);
+            // We don't want to go to sleep while in Ecm
+            mWakeLock.acquire();
         }
     }
 
     private void handleExitEmergencyCallbackMode(Message msg) {
-        Log.d(LOG_TAG, "Event EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE Received");
         AsyncResult ar = (AsyncResult)msg.obj;
-
-        // Remove pending exit ECM runnable, if any
+        if (DBG) {
+            Log.d(LOG_TAG, "handleExitEmergencyCallbackMode,ar.exception , mIsPhoneInEcmState "
+                    + ar.exception + mIsPhoneInEcmState);
+        }
+        // Remove pending exit Ecm runnable, if any
         h.removeCallbacks(mExitEcmRunnable);
 
-        if (mECMExitRespRegistrant != null) {
-            mECMExitRespRegistrant.notifyRegistrant(ar);
+        if (mEcmExitRespRegistrant != null) {
+            mEcmExitRespRegistrant.notifyRegistrant(ar);
         }
         // if exiting ecm success
         if (ar.exception == null) {
-            if (mIsPhoneInECMState) {
-                mIsPhoneInECMState = false;
+            if (mIsPhoneInEcmState) {
+                mIsPhoneInEcmState = false;
                 setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "false");
             }
             // send an Intent
@@ -903,6 +930,42 @@
         }
     }
 
+    /**
+     * Handle to cancel or restart Ecm timer in emergency call back mode
+     * if action is CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled;
+     * otherwise, restart Ecm timer and notify apps the timer is restarted.
+     */
+    void handleTimerInEmergencyCallbackMode(int action) {
+        switch(action) {
+        case CANCEL_ECM_TIMER:
+            h.removeCallbacks(mExitEcmRunnable);
+            mEcmTimerResetRegistrants.notifyResult(new Boolean(true));
+            break;
+        case RESTART_ECM_TIMER:
+            long delayInMillis = SystemProperties.getLong(
+                    TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE);
+            h.postDelayed(mExitEcmRunnable, delayInMillis);
+            mEcmTimerResetRegistrants.notifyResult(new Boolean(false));
+            break;
+        default:
+            Log.e(LOG_TAG, "handleTimerInEmergencyCallbackMode, unsupported action " + action);
+        }
+    }
+
+    /**
+     * Registration point for Ecm timer reset
+     * @param h handler to notify
+     * @param what User-defined message code
+     * @param obj placed in Message.obj
+     */
+    public void registerForEcmTimerReset(Handler h, int what, Object obj) {
+        mEcmTimerResetRegistrants.addUnique(h, what, obj);
+    }
+
+    public void unregisterForEcmTimerReset(Handler h) {
+        mEcmTimerResetRegistrants.remove(h);
+    }
+
     //***** Inner Classes
     class MyHandler extends Handler {
         MyHandler() {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
old mode 100644
new mode 100755
index be4763c..7bbf91d
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -72,7 +72,7 @@
 
     CdmaConnection pendingMO;
     boolean hangupPendingMO;
-    boolean pendingCallInECM=false;
+    boolean pendingCallInEcm=false;
     CDMAPhone phone;
 
     boolean desiredMute = false;    // false = mute off
@@ -80,6 +80,7 @@
     int pendingCallClirMode;
     Phone.State state = Phone.State.IDLE;
 
+    private boolean mIsEcmTimerCanceled = false;
 
 //    boolean needsPoll;
 
@@ -182,6 +183,14 @@
             throw new CallStateException("cannot dial in current state");
         }
 
+        String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
+        boolean isPhoneInEcmMode = inEcm.equals("true");
+        boolean isEmergencyCall = PhoneNumberUtils.isEmergencyNumber(dialString);
+
+        // Cancel Ecm timer if a second emergency call is originating in Ecm mode
+        if (isPhoneInEcmMode && isEmergencyCall) {
+            handleEcmTimer(phone.CANCEL_ECM_TIMER);
+        }
 
         // We are initiating a call therefore even if we previously
         // didn't know the state (i.e. Generic was true) we now know
@@ -210,14 +219,14 @@
             // Always unmute when initiating a new call
             setMute(false);
 
-            String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
-            if(inEcm.equals("false")) {
+            // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
+            if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
                 cm.dial(pendingMO.address, clirMode, obtainCompleteMessage());
             } else {
                 phone.exitEmergencyCallbackMode();
                 phone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null);
                 pendingCallClirMode=clirMode;
-                pendingCallInECM=true;
+                pendingCallInEcm=true;
             }
         }
 
@@ -479,6 +488,11 @@
                     // Someone has already asked to hangup this call
                     if (hangupPendingMO) {
                         hangupPendingMO = false;
+                        // Re-start Ecm timer when an uncompleted emergency call ends
+                        if (mIsEcmTimerCanceled) {
+                            handleEcmTimer(phone.RESTART_ECM_TIMER);
+                        }
+
                         try {
                             if (Phone.DEBUG_PHONE) log(
                                     "poll: hangupPendingMO, hangup conn " + i);
@@ -532,6 +546,12 @@
                     }
                 }
                 foregroundCall.setGeneric(false);
+
+                // Re-start Ecm timer when the connected emergency call ends
+                if (mIsEcmTimerCanceled) {
+                    handleEcmTimer(phone.RESTART_ECM_TIMER);
+                }
+
                 // Dropped connections are removed from the CallTracker
                 // list but kept in the Call list
                 connections[i] = null;
@@ -571,8 +591,8 @@
             droppedDuringPoll.add(pendingMO);
             pendingMO = null;
             hangupPendingMO = false;
-            if( pendingCallInECM) {
-                pendingCallInECM = false;
+            if( pendingCallInEcm) {
+                pendingCallInEcm = false;
             }
         }
 
@@ -941,9 +961,9 @@
 
             case EVENT_EXIT_ECM_RESPONSE_CDMA:
                //no matter the result, we still do the same here
-               if (pendingCallInECM) {
+               if (pendingCallInEcm) {
                    cm.dial(pendingMO.address, pendingCallClirMode, obtainCompleteMessage());
-                   pendingCallInECM = false;
+                   pendingCallInEcm = false;
                }
                phone.unsetOnEcbModeExitResponse(this);
             break;
@@ -971,6 +991,19 @@
         }
     }
 
+    /**
+     * Handle Ecm timer to be canceled or re-started
+     */
+    private void handleEcmTimer(int action) {
+        phone.handleTimerInEmergencyCallbackMode(action);
+        switch(action) {
+        case CDMAPhone.CANCEL_ECM_TIMER: mIsEcmTimerCanceled = true; break;
+        case CDMAPhone.RESTART_ECM_TIMER: mIsEcmTimerCanceled = false; break;
+        default:
+            Log.e(LOG_TAG, "handleEcmTimer, unsupported action " + action);
+        }
+    }
+
     protected void log(String msg) {
         Log.d(LOG_TAG, "[CdmaCallTracker] " + msg);
     }