Home | History | Annotate | Download | only in cdma
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.internal.telephony.cdma;
     18 
     19 import android.os.AsyncResult;
     20 import android.os.Handler;
     21 import android.os.Message;
     22 import android.os.Registrant;
     23 import android.os.RegistrantList;
     24 import android.telephony.DisconnectCause;
     25 import android.telephony.PhoneNumberUtils;
     26 import android.telephony.ServiceState;
     27 import android.telephony.Rlog;
     28 import android.os.SystemProperties;
     29 import android.text.TextUtils;
     30 
     31 import com.android.internal.telephony.CallStateException;
     32 import com.android.internal.telephony.CallTracker;
     33 import com.android.internal.telephony.CommandsInterface;
     34 import com.android.internal.telephony.Connection;
     35 import com.android.internal.telephony.DriverCall;
     36 import com.android.internal.telephony.Phone;
     37 import com.android.internal.telephony.PhoneBase;
     38 import com.android.internal.telephony.PhoneConstants;
     39 import com.android.internal.telephony.TelephonyProperties;
     40 
     41 import java.io.FileDescriptor;
     42 import java.io.PrintWriter;
     43 import java.util.ArrayList;
     44 import java.util.List;
     45 
     46 
     47 /**
     48  * {@hide}
     49  */
     50 public final class CdmaCallTracker extends CallTracker {
     51     static final String LOG_TAG = "CdmaCallTracker";
     52 
     53     private static final boolean REPEAT_POLLING = false;
     54 
     55     private static final boolean DBG_POLL = false;
     56 
     57     //***** Constants
     58 
     59     static final int MAX_CONNECTIONS = 8;
     60     static final int MAX_CONNECTIONS_PER_CALL = 1; // only 1 connection allowed per call
     61 
     62     //***** Instance Variables
     63 
     64     CdmaConnection mConnections[] = new CdmaConnection[MAX_CONNECTIONS];
     65     RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
     66     RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
     67     RegistrantList mCallWaitingRegistrants =  new RegistrantList();
     68 
     69 
     70     // connections dropped during last poll
     71     ArrayList<CdmaConnection> mDroppedDuringPoll
     72         = new ArrayList<CdmaConnection>(MAX_CONNECTIONS);
     73 
     74     CdmaCall mRingingCall = new CdmaCall(this);
     75     // A call that is ringing or (call) waiting
     76     CdmaCall mForegroundCall = new CdmaCall(this);
     77     CdmaCall mBackgroundCall = new CdmaCall(this);
     78 
     79     CdmaConnection mPendingMO;
     80     boolean mHangupPendingMO;
     81     boolean mPendingCallInEcm=false;
     82     boolean mIsInEmergencyCall = false;
     83     CDMAPhone mPhone;
     84 
     85     boolean mDesiredMute = false;    // false = mute off
     86 
     87     int mPendingCallClirMode;
     88     PhoneConstants.State mState = PhoneConstants.State.IDLE;
     89 
     90     private boolean mIsEcmTimerCanceled = false;
     91 
     92     private int m3WayCallFlashDelay = 0;
     93 //    boolean needsPoll;
     94 
     95 
     96 
     97     //***** Events
     98 
     99     //***** Constructors
    100     CdmaCallTracker(CDMAPhone phone) {
    101         mPhone = phone;
    102         mCi = phone.mCi;
    103         mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
    104         mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null);
    105         mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);
    106         mCi.registerForCallWaitingInfo(this, EVENT_CALL_WAITING_INFO_CDMA, null);
    107         mForegroundCall.setGeneric(false);
    108     }
    109 
    110     public void dispose() {
    111         Rlog.d(LOG_TAG, "CdmaCallTracker dispose");
    112         mCi.unregisterForLineControlInfo(this);
    113         mCi.unregisterForCallStateChanged(this);
    114         mCi.unregisterForOn(this);
    115         mCi.unregisterForNotAvailable(this);
    116         mCi.unregisterForCallWaitingInfo(this);
    117 
    118         clearDisconnected();
    119 
    120     }
    121 
    122     @Override
    123     protected void finalize() {
    124         Rlog.d(LOG_TAG, "CdmaCallTracker finalized");
    125     }
    126 
    127     //***** Instance Methods
    128 
    129     //***** Public Methods
    130     @Override
    131     public void registerForVoiceCallStarted(Handler h, int what, Object obj) {
    132         Registrant r = new Registrant(h, what, obj);
    133         mVoiceCallStartedRegistrants.add(r);
    134         // Notify if in call when registering
    135         if (mState != PhoneConstants.State.IDLE) {
    136             r.notifyRegistrant(new AsyncResult(null, null, null));
    137         }
    138     }
    139     @Override
    140     public void unregisterForVoiceCallStarted(Handler h) {
    141         mVoiceCallStartedRegistrants.remove(h);
    142     }
    143 
    144     @Override
    145     public void registerForVoiceCallEnded(Handler h, int what, Object obj) {
    146         Registrant r = new Registrant(h, what, obj);
    147         mVoiceCallEndedRegistrants.add(r);
    148     }
    149 
    150     @Override
    151     public void unregisterForVoiceCallEnded(Handler h) {
    152         mVoiceCallEndedRegistrants.remove(h);
    153     }
    154 
    155     public void registerForCallWaiting(Handler h, int what, Object obj) {
    156         Registrant r = new Registrant (h, what, obj);
    157         mCallWaitingRegistrants.add(r);
    158     }
    159 
    160     public void unregisterForCallWaiting(Handler h) {
    161         mCallWaitingRegistrants.remove(h);
    162     }
    163 
    164     /**
    165      * clirMode is one of the CLIR_ constants
    166      */
    167     Connection
    168     dial (String dialString, int clirMode) throws CallStateException {
    169         // note that this triggers call state changed notif
    170         clearDisconnected();
    171 
    172         if (!canDial()) {
    173             throw new CallStateException("cannot dial in current state");
    174         }
    175 
    176         String origNumber = dialString;
    177         String operatorIsoContry = mPhone.getSystemProperty(
    178                 TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
    179         String simIsoContry = mPhone.getSystemProperty(
    180                 TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
    181         boolean internationalRoaming = !TextUtils.isEmpty(operatorIsoContry)
    182                 && !TextUtils.isEmpty(simIsoContry)
    183                 && !simIsoContry.equals(operatorIsoContry);
    184         if (internationalRoaming) {
    185             if ("us".equals(simIsoContry)) {
    186                 internationalRoaming = internationalRoaming && !"vi".equals(operatorIsoContry);
    187             } else if ("vi".equals(simIsoContry)) {
    188                 internationalRoaming = internationalRoaming && !"us".equals(operatorIsoContry);
    189             }
    190         }
    191         if (internationalRoaming) {
    192             dialString = convertNumberIfNecessary(mPhone, dialString);
    193         }
    194 
    195         String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
    196         boolean isPhoneInEcmMode = inEcm.equals("true");
    197         boolean isEmergencyCall =
    198                 PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString);
    199 
    200         // Cancel Ecm timer if a second emergency call is originating in Ecm mode
    201         if (isPhoneInEcmMode && isEmergencyCall) {
    202             handleEcmTimer(CDMAPhone.CANCEL_ECM_TIMER);
    203         }
    204 
    205         // We are initiating a call therefore even if we previously
    206         // didn't know the state (i.e. Generic was true) we now know
    207         // and therefore can set Generic to false.
    208         mForegroundCall.setGeneric(false);
    209 
    210         // The new call must be assigned to the foreground call.
    211         // That call must be idle, so place anything that's
    212         // there on hold
    213         if (mForegroundCall.getState() == CdmaCall.State.ACTIVE) {
    214             return dialThreeWay(dialString);
    215         }
    216 
    217         mPendingMO = new CdmaConnection(mPhone.getContext(), checkForTestEmergencyNumber(dialString),
    218                 this, mForegroundCall);
    219         mHangupPendingMO = false;
    220 
    221         if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
    222                 || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0 ) {
    223             // Phone number is invalid
    224             mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;
    225 
    226             // handlePollCalls() will notice this call not present
    227             // and will mark it as dropped.
    228             pollCallsWhenSafe();
    229         } else {
    230             // Always unmute when initiating a new call
    231             setMute(false);
    232 
    233             // Check data call
    234             disableDataCallInEmergencyCall(dialString);
    235 
    236             // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
    237             if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
    238                 mCi.dial(mPendingMO.getAddress(), clirMode, obtainCompleteMessage());
    239             } else {
    240                 mPhone.exitEmergencyCallbackMode();
    241                 mPhone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null);
    242                 mPendingCallClirMode=clirMode;
    243                 mPendingCallInEcm=true;
    244             }
    245         }
    246 
    247         if (mNumberConverted) {
    248             mPendingMO.setConverted(origNumber);
    249             mNumberConverted = false;
    250         }
    251 
    252         updatePhoneState();
    253         mPhone.notifyPreciseCallStateChanged();
    254 
    255         return mPendingMO;
    256     }
    257 
    258 
    259     Connection
    260     dial (String dialString) throws CallStateException {
    261         return dial(dialString, CommandsInterface.CLIR_DEFAULT);
    262     }
    263 
    264     private Connection
    265     dialThreeWay (String dialString) {
    266         if (!mForegroundCall.isIdle()) {
    267             // Check data call
    268             disableDataCallInEmergencyCall(dialString);
    269 
    270             // Attach the new connection to foregroundCall
    271             mPendingMO = new CdmaConnection(mPhone.getContext(),
    272                                 checkForTestEmergencyNumber(dialString), this, mForegroundCall);
    273             // Some network need a empty flash before sending the normal one
    274             m3WayCallFlashDelay = mPhone.getContext().getResources()
    275                     .getInteger(com.android.internal.R.integer.config_cdma_3waycall_flash_delay);
    276             if (m3WayCallFlashDelay > 0) {
    277                 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_THREE_WAY_DIAL_BLANK_FLASH));
    278             } else {
    279                 mCi.sendCDMAFeatureCode(mPendingMO.getAddress(),
    280                         obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA));
    281             }
    282             return mPendingMO;
    283         }
    284         return null;
    285     }
    286 
    287     void
    288     acceptCall() throws CallStateException {
    289         if (mRingingCall.getState() == CdmaCall.State.INCOMING) {
    290             Rlog.i("phone", "acceptCall: incoming...");
    291             // Always unmute when answering a new call
    292             setMute(false);
    293             mCi.acceptCall(obtainCompleteMessage());
    294         } else if (mRingingCall.getState() == CdmaCall.State.WAITING) {
    295             CdmaConnection cwConn = (CdmaConnection)(mRingingCall.getLatestConnection());
    296 
    297             // Since there is no network response for supplimentary
    298             // service for CDMA, we assume call waiting is answered.
    299             // ringing Call state change to idle is in CdmaCall.detach
    300             // triggered by updateParent.
    301             cwConn.updateParent(mRingingCall, mForegroundCall);
    302             cwConn.onConnectedInOrOut();
    303             updatePhoneState();
    304             switchWaitingOrHoldingAndActive();
    305         } else {
    306             throw new CallStateException("phone not ringing");
    307         }
    308     }
    309 
    310     void
    311     rejectCall () throws CallStateException {
    312         // AT+CHLD=0 means "release held or UDUB"
    313         // so if the phone isn't ringing, this could hang up held
    314         if (mRingingCall.getState().isRinging()) {
    315             mCi.rejectCall(obtainCompleteMessage());
    316         } else {
    317             throw new CallStateException("phone not ringing");
    318         }
    319     }
    320 
    321     void
    322     switchWaitingOrHoldingAndActive() throws CallStateException {
    323         // Should we bother with this check?
    324         if (mRingingCall.getState() == CdmaCall.State.INCOMING) {
    325             throw new CallStateException("cannot be in the incoming state");
    326         } else if (mForegroundCall.getConnections().size() > 1) {
    327             flashAndSetGenericTrue();
    328         } else {
    329             // Send a flash command to CDMA network for putting the other party on hold.
    330             // For CDMA networks which do not support this the user would just hear a beep
    331             // from the network. For CDMA networks which do support it will put the other
    332             // party on hold.
    333             mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT));
    334         }
    335     }
    336 
    337     void
    338     conference() {
    339         // Should we be checking state?
    340         flashAndSetGenericTrue();
    341     }
    342 
    343     void
    344     explicitCallTransfer() {
    345         mCi.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT));
    346     }
    347 
    348     void
    349     clearDisconnected() {
    350         internalClearDisconnected();
    351 
    352         updatePhoneState();
    353         mPhone.notifyPreciseCallStateChanged();
    354     }
    355 
    356     boolean
    357     canConference() {
    358         return mForegroundCall.getState() == CdmaCall.State.ACTIVE
    359                 && mBackgroundCall.getState() == CdmaCall.State.HOLDING
    360                 && !mBackgroundCall.isFull()
    361                 && !mForegroundCall.isFull();
    362     }
    363 
    364     boolean
    365     canDial() {
    366         boolean ret;
    367         int serviceState = mPhone.getServiceState().getState();
    368         String disableCall = SystemProperties.get(
    369                 TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
    370 
    371         ret = (serviceState != ServiceState.STATE_POWER_OFF)
    372                 && mPendingMO == null
    373                 && !mRingingCall.isRinging()
    374                 && !disableCall.equals("true")
    375                 && (!mForegroundCall.getState().isAlive()
    376                     || (mForegroundCall.getState() == CdmaCall.State.ACTIVE)
    377                     || !mBackgroundCall.getState().isAlive());
    378 
    379         if (!ret) {
    380             log(String.format("canDial is false\n" +
    381                               "((serviceState=%d) != ServiceState.STATE_POWER_OFF)::=%s\n" +
    382                               "&& pendingMO == null::=%s\n" +
    383                               "&& !ringingCall.isRinging()::=%s\n" +
    384                               "&& !disableCall.equals(\"true\")::=%s\n" +
    385                               "&& (!foregroundCall.getState().isAlive()::=%s\n" +
    386                               "   || foregroundCall.getState() == CdmaCall.State.ACTIVE::=%s\n" +
    387                               "   ||!backgroundCall.getState().isAlive())::=%s)",
    388                     serviceState,
    389                     serviceState != ServiceState.STATE_POWER_OFF,
    390                     mPendingMO == null,
    391                     !mRingingCall.isRinging(),
    392                     !disableCall.equals("true"),
    393                     !mForegroundCall.getState().isAlive(),
    394                     mForegroundCall.getState() == CdmaCall.State.ACTIVE,
    395                     !mBackgroundCall.getState().isAlive()));
    396         }
    397         return ret;
    398     }
    399 
    400     boolean
    401     canTransfer() {
    402         Rlog.e(LOG_TAG, "canTransfer: not possible in CDMA");
    403         return false;
    404     }
    405 
    406     //***** Private Instance Methods
    407 
    408     private void
    409     internalClearDisconnected() {
    410         mRingingCall.clearDisconnected();
    411         mForegroundCall.clearDisconnected();
    412         mBackgroundCall.clearDisconnected();
    413     }
    414 
    415     /**
    416      * Obtain a message to use for signalling "invoke getCurrentCalls() when
    417      * this operation and all other pending operations are complete
    418      */
    419     private Message
    420     obtainCompleteMessage() {
    421         return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
    422     }
    423 
    424     /**
    425      * Obtain a message to use for signalling "invoke getCurrentCalls() when
    426      * this operation and all other pending operations are complete
    427      */
    428     private Message
    429     obtainCompleteMessage(int what) {
    430         mPendingOperations++;
    431         mLastRelevantPoll = null;
    432         mNeedsPoll = true;
    433 
    434         if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" +
    435                 mPendingOperations + ", needsPoll=" + mNeedsPoll);
    436 
    437         return obtainMessage(what);
    438     }
    439 
    440     private void
    441     operationComplete() {
    442         mPendingOperations--;
    443 
    444         if (DBG_POLL) log("operationComplete: pendingOperations=" +
    445                 mPendingOperations + ", needsPoll=" + mNeedsPoll);
    446 
    447         if (mPendingOperations == 0 && mNeedsPoll) {
    448             mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
    449             mCi.getCurrentCalls(mLastRelevantPoll);
    450         } else if (mPendingOperations < 0) {
    451             // this should never happen
    452             Rlog.e(LOG_TAG,"CdmaCallTracker.pendingOperations < 0");
    453             mPendingOperations = 0;
    454         }
    455     }
    456 
    457 
    458 
    459     private void
    460     updatePhoneState() {
    461         PhoneConstants.State oldState = mState;
    462 
    463         if (mRingingCall.isRinging()) {
    464             mState = PhoneConstants.State.RINGING;
    465         } else if (mPendingMO != null ||
    466                 !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
    467             mState = PhoneConstants.State.OFFHOOK;
    468         } else {
    469             mState = PhoneConstants.State.IDLE;
    470         }
    471 
    472         if (mState == PhoneConstants.State.IDLE && oldState != mState) {
    473             mVoiceCallEndedRegistrants.notifyRegistrants(
    474                 new AsyncResult(null, null, null));
    475         } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
    476             mVoiceCallStartedRegistrants.notifyRegistrants (
    477                     new AsyncResult(null, null, null));
    478         }
    479         if (Phone.DEBUG_PHONE) {
    480             log("update phone state, old=" + oldState + " new="+ mState);
    481         }
    482         if (mState != oldState) {
    483             mPhone.notifyPhoneStateChanged();
    484         }
    485     }
    486 
    487     // ***** Overwritten from CallTracker
    488 
    489     @Override
    490     protected void
    491     handlePollCalls(AsyncResult ar) {
    492         List polledCalls;
    493 
    494         if (ar.exception == null) {
    495             polledCalls = (List)ar.result;
    496         } else if (isCommandExceptionRadioNotAvailable(ar.exception)) {
    497             // just a dummy empty ArrayList to cause the loop
    498             // to hang up all the calls
    499             polledCalls = new ArrayList();
    500         } else {
    501             // Radio probably wasn't ready--try again in a bit
    502             // But don't keep polling if the channel is closed
    503             pollCallsAfterDelay();
    504             return;
    505         }
    506 
    507         Connection newRinging = null; //or waiting
    508         Connection newUnknown = null;
    509         boolean hasNonHangupStateChanged = false;   // Any change besides
    510                                                     // a dropped connection
    511         boolean hasAnyCallDisconnected = false;
    512         boolean needsPollDelay = false;
    513         boolean unknownConnectionAppeared = false;
    514 
    515         for (int i = 0, curDC = 0, dcSize = polledCalls.size()
    516                 ; i < mConnections.length; i++) {
    517             CdmaConnection conn = mConnections[i];
    518             DriverCall dc = null;
    519 
    520             // polledCall list is sparse
    521             if (curDC < dcSize) {
    522                 dc = (DriverCall) polledCalls.get(curDC);
    523 
    524                 if (dc.index == i+1) {
    525                     curDC++;
    526                 } else {
    527                     dc = null;
    528                 }
    529             }
    530 
    531             if (DBG_POLL) log("poll: conn[i=" + i + "]=" +
    532                     conn+", dc=" + dc);
    533 
    534             if (conn == null && dc != null) {
    535                 // Connection appeared in CLCC response that we don't know about
    536                 if (mPendingMO != null && mPendingMO.compareTo(dc)) {
    537 
    538                     if (DBG_POLL) log("poll: pendingMO=" + mPendingMO);
    539 
    540                     // It's our pending mobile originating call
    541                     mConnections[i] = mPendingMO;
    542                     mPendingMO.mIndex = i;
    543                     mPendingMO.update(dc);
    544                     mPendingMO = null;
    545 
    546                     // Someone has already asked to hangup this call
    547                     if (mHangupPendingMO) {
    548                         mHangupPendingMO = false;
    549                         // Re-start Ecm timer when an uncompleted emergency call ends
    550                         if (mIsEcmTimerCanceled) {
    551                             handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER);
    552                         }
    553 
    554                         try {
    555                             if (Phone.DEBUG_PHONE) log(
    556                                     "poll: hangupPendingMO, hangup conn " + i);
    557                             hangup(mConnections[i]);
    558                         } catch (CallStateException ex) {
    559                             Rlog.e(LOG_TAG, "unexpected error on hangup");
    560                         }
    561 
    562                         // Do not continue processing this poll
    563                         // Wait for hangup and repoll
    564                         return;
    565                     }
    566                 } else {
    567                     if (Phone.DEBUG_PHONE) {
    568                         log("pendingMo=" + mPendingMO + ", dc=" + dc);
    569                     }
    570                     mConnections[i] = new CdmaConnection(mPhone.getContext(), dc, this, i);
    571 
    572                     if (mHandoverConnection != null) {
    573                         // Single Radio Voice Call Continuity (SRVCC) completed
    574                         mPhone.migrateFrom((PhoneBase) mPhone.getImsPhone());
    575                         mConnections[i].migrateFrom(mHandoverConnection);
    576                         mPhone.notifyHandoverStateChanged(mConnections[i]);
    577                         mHandoverConnection = null;
    578                     } else {
    579                         // find if the MT call is a new ring or unknown connection
    580                         newRinging = checkMtFindNewRinging(dc,i);
    581                         if (newRinging == null) {
    582                             unknownConnectionAppeared = true;
    583                         }
    584                     }
    585                     checkAndEnableDataCallAfterEmergencyCallDropped();
    586                 }
    587                 hasNonHangupStateChanged = true;
    588             } else if (conn != null && dc == null) {
    589                 // This case means the RIL has no more active call anymore and
    590                 // we need to clean up the foregroundCall and ringingCall.
    591                 // Loop through foreground call connections as
    592                 // it contains the known logical connections.
    593                 int count = mForegroundCall.mConnections.size();
    594                 for (int n = 0; n < count; n++) {
    595                     if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll");
    596                     CdmaConnection cn = (CdmaConnection)mForegroundCall.mConnections.get(n);
    597                     mDroppedDuringPoll.add(cn);
    598                 }
    599                 count = mRingingCall.mConnections.size();
    600                 // Loop through ringing call connections as
    601                 // it may contain the known logical connections.
    602                 for (int n = 0; n < count; n++) {
    603                     if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll");
    604                     CdmaConnection cn = (CdmaConnection)mRingingCall.mConnections.get(n);
    605                     mDroppedDuringPoll.add(cn);
    606                 }
    607                 mForegroundCall.setGeneric(false);
    608                 mRingingCall.setGeneric(false);
    609 
    610                 // Re-start Ecm timer when the connected emergency call ends
    611                 if (mIsEcmTimerCanceled) {
    612                     handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER);
    613                 }
    614                 // If emergency call is not going through while dialing
    615                 checkAndEnableDataCallAfterEmergencyCallDropped();
    616 
    617                 // Dropped connections are removed from the CallTracker
    618                 // list but kept in the Call list
    619                 mConnections[i] = null;
    620             } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
    621                 // Call collision case
    622                 if (conn.isIncoming() != dc.isMT) {
    623                     if (dc.isMT == true){
    624                         // Mt call takes precedence than Mo,drops Mo
    625                         mDroppedDuringPoll.add(conn);
    626                         // find if the MT call is a new ring or unknown connection
    627                         newRinging = checkMtFindNewRinging(dc,i);
    628                         if (newRinging == null) {
    629                             unknownConnectionAppeared = true;
    630                             newUnknown = conn;
    631                         }
    632                         checkAndEnableDataCallAfterEmergencyCallDropped();
    633                     } else {
    634                         // Call info stored in conn is not consistent with the call info from dc.
    635                         // We should follow the rule of MT calls taking precedence over MO calls
    636                         // when there is conflict, so here we drop the call info from dc and
    637                         // continue to use the call info from conn, and only take a log.
    638                         Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc);
    639                     }
    640                 } else {
    641                     boolean changed;
    642                     changed = conn.update(dc);
    643                     hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
    644                 }
    645             }
    646 
    647             if (REPEAT_POLLING) {
    648                 if (dc != null) {
    649                     // FIXME with RIL, we should not need this anymore
    650                     if ((dc.state == DriverCall.State.DIALING
    651                             /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/)
    652                         || (dc.state == DriverCall.State.ALERTING
    653                             /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/)
    654                         || (dc.state == DriverCall.State.INCOMING
    655                             /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/)
    656                         || (dc.state == DriverCall.State.WAITING
    657                             /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)
    658                     ) {
    659                         // Sometimes there's no unsolicited notification
    660                         // for state transitions
    661                         needsPollDelay = true;
    662                     }
    663                 }
    664             }
    665         }
    666 
    667         // This is the first poll after an ATD.
    668         // We expect the pending call to appear in the list
    669         // If it does not, we land here
    670         if (mPendingMO != null) {
    671             Rlog.d(LOG_TAG,"Pending MO dropped before poll fg state:"
    672                             + mForegroundCall.getState());
    673 
    674             mDroppedDuringPoll.add(mPendingMO);
    675             mPendingMO = null;
    676             mHangupPendingMO = false;
    677             if( mPendingCallInEcm) {
    678                 mPendingCallInEcm = false;
    679             }
    680         }
    681 
    682         if (newRinging != null) {
    683             mPhone.notifyNewRingingConnection(newRinging);
    684         }
    685 
    686         // clear the "local hangup" and "missed/rejected call"
    687         // cases from the "dropped during poll" list
    688         // These cases need no "last call fail" reason
    689         for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
    690             CdmaConnection conn = mDroppedDuringPoll.get(i);
    691 
    692             if (conn.isIncoming() && conn.getConnectTime() == 0) {
    693                 // Missed or rejected call
    694                 int cause;
    695                 if (conn.mCause == DisconnectCause.LOCAL) {
    696                     cause = DisconnectCause.INCOMING_REJECTED;
    697                 } else {
    698                     cause = DisconnectCause.INCOMING_MISSED;
    699                 }
    700 
    701                 if (Phone.DEBUG_PHONE) {
    702                     log("missed/rejected call, conn.cause=" + conn.mCause);
    703                     log("setting cause to " + cause);
    704                 }
    705                 mDroppedDuringPoll.remove(i);
    706                 hasAnyCallDisconnected |= conn.onDisconnect(cause);
    707             } else if (conn.mCause == DisconnectCause.LOCAL
    708                     || conn.mCause == DisconnectCause.INVALID_NUMBER) {
    709                 mDroppedDuringPoll.remove(i);
    710                 hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
    711             }
    712         }
    713 
    714         // Any non-local disconnects: determine cause
    715         if (mDroppedDuringPoll.size() > 0) {
    716             mCi.getLastCallFailCause(
    717                 obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
    718         }
    719 
    720         if (needsPollDelay) {
    721             pollCallsAfterDelay();
    722         }
    723 
    724         // Cases when we can no longer keep disconnected Connection's
    725         // with their previous calls
    726         // 1) the phone has started to ring
    727         // 2) A Call/Connection object has changed state...
    728         //    we may have switched or held or answered (but not hung up)
    729         if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) {
    730             internalClearDisconnected();
    731         }
    732 
    733         updatePhoneState();
    734 
    735         if (unknownConnectionAppeared) {
    736             mPhone.notifyUnknownConnection(newUnknown);
    737         }
    738 
    739         if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
    740             mPhone.notifyPreciseCallStateChanged();
    741         }
    742 
    743         //dumpState();
    744     }
    745 
    746     //***** Called from CdmaConnection
    747     /*package*/ void
    748     hangup (CdmaConnection conn) throws CallStateException {
    749         if (conn.mOwner != this) {
    750             throw new CallStateException ("CdmaConnection " + conn
    751                                     + "does not belong to CdmaCallTracker " + this);
    752         }
    753 
    754         if (conn == mPendingMO) {
    755             // We're hanging up an outgoing call that doesn't have it's
    756             // GSM index assigned yet
    757 
    758             if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true");
    759             mHangupPendingMO = true;
    760         } else if ((conn.getCall() == mRingingCall)
    761                 && (mRingingCall.getState() == CdmaCall.State.WAITING)) {
    762             // Handle call waiting hang up case.
    763             //
    764             // The ringingCall state will change to IDLE in CdmaCall.detach
    765             // if the ringing call connection size is 0. We don't specifically
    766             // set the ringing call state to IDLE here to avoid a race condition
    767             // where a new call waiting could get a hang up from an old call
    768             // waiting ringingCall.
    769             //
    770             // PhoneApp does the call log itself since only PhoneApp knows
    771             // the hangup reason is user ignoring or timing out. So conn.onDisconnect()
    772             // is not called here. Instead, conn.onLocalDisconnect() is called.
    773             conn.onLocalDisconnect();
    774             updatePhoneState();
    775             mPhone.notifyPreciseCallStateChanged();
    776             return;
    777         } else {
    778             try {
    779                 mCi.hangupConnection (conn.getCDMAIndex(), obtainCompleteMessage());
    780             } catch (CallStateException ex) {
    781                 // Ignore "connection not found"
    782                 // Call may have hung up already
    783                 Rlog.w(LOG_TAG,"CdmaCallTracker WARN: hangup() on absent connection "
    784                                 + conn);
    785             }
    786         }
    787 
    788         conn.onHangupLocal();
    789     }
    790 
    791     /*package*/ void
    792     separate (CdmaConnection conn) throws CallStateException {
    793         if (conn.mOwner != this) {
    794             throw new CallStateException ("CdmaConnection " + conn
    795                                     + "does not belong to CdmaCallTracker " + this);
    796         }
    797         try {
    798             mCi.separateConnection (conn.getCDMAIndex(),
    799                 obtainCompleteMessage(EVENT_SEPARATE_RESULT));
    800         } catch (CallStateException ex) {
    801             // Ignore "connection not found"
    802             // Call may have hung up already
    803             Rlog.w(LOG_TAG,"CdmaCallTracker WARN: separate() on absent connection "
    804                           + conn);
    805         }
    806     }
    807 
    808     //***** Called from CDMAPhone
    809 
    810     /*package*/ void
    811     setMute(boolean mute) {
    812         mDesiredMute = mute;
    813         mCi.setMute(mDesiredMute, null);
    814     }
    815 
    816     /*package*/ boolean
    817     getMute() {
    818         return mDesiredMute;
    819     }
    820 
    821 
    822     //***** Called from CdmaCall
    823 
    824     /* package */ void
    825     hangup (CdmaCall call) throws CallStateException {
    826         if (call.getConnections().size() == 0) {
    827             throw new CallStateException("no connections in call");
    828         }
    829 
    830         if (call == mRingingCall) {
    831             if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background");
    832             mCi.hangupWaitingOrBackground(obtainCompleteMessage());
    833         } else if (call == mForegroundCall) {
    834             if (call.isDialingOrAlerting()) {
    835                 if (Phone.DEBUG_PHONE) {
    836                     log("(foregnd) hangup dialing or alerting...");
    837                 }
    838                 hangup((CdmaConnection)(call.getConnections().get(0)));
    839             } else {
    840                 hangupForegroundResumeBackground();
    841             }
    842         } else if (call == mBackgroundCall) {
    843             if (mRingingCall.isRinging()) {
    844                 if (Phone.DEBUG_PHONE) {
    845                     log("hangup all conns in background call");
    846                 }
    847                 hangupAllConnections(call);
    848             } else {
    849                 hangupWaitingOrBackground();
    850             }
    851         } else {
    852             throw new RuntimeException ("CdmaCall " + call +
    853                     "does not belong to CdmaCallTracker " + this);
    854         }
    855 
    856         call.onHangupLocal();
    857         mPhone.notifyPreciseCallStateChanged();
    858     }
    859 
    860     /* package */
    861     void hangupWaitingOrBackground() {
    862         if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground");
    863         mCi.hangupWaitingOrBackground(obtainCompleteMessage());
    864     }
    865 
    866     /* package */
    867     void hangupForegroundResumeBackground() {
    868         if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground");
    869         mCi.hangupForegroundResumeBackground(obtainCompleteMessage());
    870     }
    871 
    872     void hangupConnectionByIndex(CdmaCall call, int index)
    873             throws CallStateException {
    874         int count = call.mConnections.size();
    875         for (int i = 0; i < count; i++) {
    876             CdmaConnection cn = (CdmaConnection)call.mConnections.get(i);
    877             if (cn.getCDMAIndex() == index) {
    878                 mCi.hangupConnection(index, obtainCompleteMessage());
    879                 return;
    880             }
    881         }
    882 
    883         throw new CallStateException("no gsm index found");
    884     }
    885 
    886     void hangupAllConnections(CdmaCall call) {
    887         try {
    888             int count = call.mConnections.size();
    889             for (int i = 0; i < count; i++) {
    890                 CdmaConnection cn = (CdmaConnection)call.mConnections.get(i);
    891                 mCi.hangupConnection(cn.getCDMAIndex(), obtainCompleteMessage());
    892             }
    893         } catch (CallStateException ex) {
    894             Rlog.e(LOG_TAG, "hangupConnectionByIndex caught " + ex);
    895         }
    896     }
    897 
    898     /* package */
    899     CdmaConnection getConnectionByIndex(CdmaCall call, int index)
    900             throws CallStateException {
    901         int count = call.mConnections.size();
    902         for (int i = 0; i < count; i++) {
    903             CdmaConnection cn = (CdmaConnection)call.mConnections.get(i);
    904             if (cn.getCDMAIndex() == index) {
    905                 return cn;
    906             }
    907         }
    908 
    909         return null;
    910     }
    911 
    912     private void flashAndSetGenericTrue() {
    913         mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT));
    914 
    915         // Set generic to true because in CDMA it is not known what
    916         // the status of the call is after a call waiting is answered,
    917         // 3 way call merged or a switch between calls.
    918         mForegroundCall.setGeneric(true);
    919         mPhone.notifyPreciseCallStateChanged();
    920     }
    921 
    922     private void handleRadioNotAvailable() {
    923         // handlePollCalls will clear out its
    924         // call list when it gets the CommandException
    925         // error result from this
    926         pollCallsWhenSafe();
    927     }
    928 
    929     private void notifyCallWaitingInfo(CdmaCallWaitingNotification obj) {
    930         if (mCallWaitingRegistrants != null) {
    931             mCallWaitingRegistrants.notifyRegistrants(new AsyncResult(null, obj, null));
    932         }
    933     }
    934 
    935     private void handleCallWaitingInfo (CdmaCallWaitingNotification cw) {
    936         // Check how many connections in foregroundCall.
    937         // If the connection in foregroundCall is more
    938         // than one, then the connection information is
    939         // not reliable anymore since it means either
    940         // call waiting is connected or 3 way call is
    941         // dialed before, so set generic.
    942         if (mForegroundCall.mConnections.size() > 1 ) {
    943             mForegroundCall.setGeneric(true);
    944         }
    945 
    946         // Create a new CdmaConnection which attaches itself to ringingCall.
    947         mRingingCall.setGeneric(false);
    948         new CdmaConnection(mPhone.getContext(), cw, this, mRingingCall);
    949         updatePhoneState();
    950 
    951         // Finally notify application
    952         notifyCallWaitingInfo(cw);
    953     }
    954     //****** Overridden from Handler
    955 
    956     @Override
    957     public void
    958     handleMessage (Message msg) {
    959         AsyncResult ar;
    960 
    961         if (!mPhone.mIsTheCurrentActivePhone) {
    962             Rlog.w(LOG_TAG, "Ignoring events received on inactive CdmaPhone");
    963             return;
    964         }
    965         switch (msg.what) {
    966             case EVENT_POLL_CALLS_RESULT:{
    967                 Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
    968                 ar = (AsyncResult)msg.obj;
    969 
    970                 if(msg == mLastRelevantPoll) {
    971                     if(DBG_POLL) log(
    972                             "handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
    973                     mNeedsPoll = false;
    974                     mLastRelevantPoll = null;
    975                     handlePollCalls((AsyncResult)msg.obj);
    976                 }
    977             }
    978             break;
    979 
    980             case EVENT_OPERATION_COMPLETE:
    981                 operationComplete();
    982             break;
    983 
    984             case EVENT_SWITCH_RESULT:
    985                  // In GSM call operationComplete() here which gets the
    986                  // current call list. But in CDMA there is no list so
    987                  // there is nothing to do.
    988             break;
    989 
    990             case EVENT_GET_LAST_CALL_FAIL_CAUSE:
    991                 int causeCode;
    992                 ar = (AsyncResult)msg.obj;
    993 
    994                 operationComplete();
    995 
    996                 if (ar.exception != null) {
    997                     // An exception occurred...just treat the disconnect
    998                     // cause as "normal"
    999                     causeCode = CallFailCause.NORMAL_CLEARING;
   1000                     Rlog.i(LOG_TAG,
   1001                             "Exception during getLastCallFailCause, assuming normal disconnect");
   1002                 } else {
   1003                     causeCode = ((int[])ar.result)[0];
   1004                 }
   1005 
   1006                 for (int i = 0, s =  mDroppedDuringPoll.size()
   1007                         ; i < s ; i++
   1008                 ) {
   1009                     CdmaConnection conn = mDroppedDuringPoll.get(i);
   1010 
   1011                     conn.onRemoteDisconnect(causeCode);
   1012                 }
   1013 
   1014                 updatePhoneState();
   1015 
   1016                 mPhone.notifyPreciseCallStateChanged();
   1017                 mDroppedDuringPoll.clear();
   1018             break;
   1019 
   1020             case EVENT_REPOLL_AFTER_DELAY:
   1021             case EVENT_CALL_STATE_CHANGE:
   1022                 pollCallsWhenSafe();
   1023             break;
   1024 
   1025             case EVENT_RADIO_AVAILABLE:
   1026                 handleRadioAvailable();
   1027             break;
   1028 
   1029             case EVENT_RADIO_NOT_AVAILABLE:
   1030                 handleRadioNotAvailable();
   1031             break;
   1032 
   1033             case EVENT_EXIT_ECM_RESPONSE_CDMA:
   1034                 // no matter the result, we still do the same here
   1035                 if (mPendingCallInEcm) {
   1036                     mCi.dial(mPendingMO.getAddress(), mPendingCallClirMode, obtainCompleteMessage());
   1037                     mPendingCallInEcm = false;
   1038                 }
   1039                 mPhone.unsetOnEcbModeExitResponse(this);
   1040             break;
   1041 
   1042             case EVENT_CALL_WAITING_INFO_CDMA:
   1043                ar = (AsyncResult)msg.obj;
   1044                if (ar.exception == null) {
   1045                    handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result);
   1046                    Rlog.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received");
   1047                }
   1048             break;
   1049 
   1050             case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA:
   1051                 ar = (AsyncResult)msg.obj;
   1052                 if (ar.exception == null) {
   1053                     // Assume 3 way call is connected
   1054                     mPendingMO.onConnectedInOrOut();
   1055                     mPendingMO = null;
   1056                 }
   1057             break;
   1058 
   1059             case EVENT_THREE_WAY_DIAL_BLANK_FLASH:
   1060                 ar = (AsyncResult) msg.obj;
   1061                 if (ar.exception == null) {
   1062                     postDelayed(
   1063                             new Runnable() {
   1064                                 public void run() {
   1065                                     if (mPendingMO != null) {
   1066                                         mCi.sendCDMAFeatureCode(mPendingMO.getAddress(),
   1067                                                 obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA));
   1068                                     }
   1069                                 }
   1070                             }, m3WayCallFlashDelay);
   1071                 } else {
   1072                     mPendingMO = null;
   1073                     Rlog.w(LOG_TAG, "exception happened on Blank Flash for 3-way call");
   1074                 }
   1075             break;
   1076 
   1077             default:{
   1078                throw new RuntimeException("unexpected event not handled");
   1079             }
   1080         }
   1081     }
   1082 
   1083     /**
   1084      * Handle Ecm timer to be canceled or re-started
   1085      */
   1086     private void handleEcmTimer(int action) {
   1087         mPhone.handleTimerInEmergencyCallbackMode(action);
   1088         switch(action) {
   1089         case CDMAPhone.CANCEL_ECM_TIMER: mIsEcmTimerCanceled = true; break;
   1090         case CDMAPhone.RESTART_ECM_TIMER: mIsEcmTimerCanceled = false; break;
   1091         default:
   1092             Rlog.e(LOG_TAG, "handleEcmTimer, unsupported action " + action);
   1093         }
   1094     }
   1095 
   1096     /**
   1097      * Disable data call when emergency call is connected
   1098      */
   1099     private void disableDataCallInEmergencyCall(String dialString) {
   1100         if (PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString)) {
   1101             if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall");
   1102             mIsInEmergencyCall = true;
   1103             mPhone.mDcTracker.setInternalDataEnabled(false);
   1104         }
   1105     }
   1106 
   1107     /**
   1108      * Check and enable data call after an emergency call is dropped if it's
   1109      * not in ECM
   1110      */
   1111     private void checkAndEnableDataCallAfterEmergencyCallDropped() {
   1112         if (mIsInEmergencyCall) {
   1113             mIsInEmergencyCall = false;
   1114             String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
   1115             if (Phone.DEBUG_PHONE) {
   1116                 log("checkAndEnableDataCallAfterEmergencyCallDropped,inEcm=" + inEcm);
   1117             }
   1118             if (inEcm.compareTo("false") == 0) {
   1119                 // Re-initiate data connection
   1120                 mPhone.mDcTracker.setInternalDataEnabled(true);
   1121             }
   1122         }
   1123     }
   1124 
   1125     /**
   1126      * Check the MT call to see if it's a new ring or
   1127      * a unknown connection.
   1128      */
   1129     private Connection checkMtFindNewRinging(DriverCall dc, int i) {
   1130 
   1131         Connection newRinging = null;
   1132 
   1133         // it's a ringing call
   1134         if (mConnections[i].getCall() == mRingingCall) {
   1135             newRinging = mConnections[i];
   1136             if (Phone.DEBUG_PHONE) log("Notify new ring " + dc);
   1137         } else {
   1138             // Something strange happened: a call which is neither
   1139             // a ringing call nor the one we created. It could be the
   1140             // call collision result from RIL
   1141             Rlog.e(LOG_TAG,"Phantom call appeared " + dc);
   1142             // If it's a connected call, set the connect time so that
   1143             // it's non-zero.  It may not be accurate, but at least
   1144             // it won't appear as a Missed Call.
   1145             if (dc.state != DriverCall.State.ALERTING
   1146                 && dc.state != DriverCall.State.DIALING) {
   1147                 mConnections[i].onConnectedInOrOut();
   1148                 if (dc.state == DriverCall.State.HOLDING) {
   1149                     // We've transitioned into HOLDING
   1150                     mConnections[i].onStartedHolding();
   1151                 }
   1152             }
   1153         }
   1154         return newRinging;
   1155     }
   1156 
   1157     /**
   1158      * Check if current call is in emergency call
   1159      *
   1160      * @return true if it is in emergency call
   1161      *         false if it is not in emergency call
   1162      */
   1163     boolean isInEmergencyCall() {
   1164         return mIsInEmergencyCall;
   1165     }
   1166 
   1167     @Override
   1168     protected void log(String msg) {
   1169         Rlog.d(LOG_TAG, "[CdmaCallTracker] " + msg);
   1170     }
   1171 
   1172     @Override
   1173     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1174         pw.println("GsmCallTracker extends:");
   1175         super.dump(fd, pw, args);
   1176         pw.println("droppedDuringPoll: length=" + mConnections.length);
   1177         for(int i=0; i < mConnections.length; i++) {
   1178             pw.printf(" mConnections[%d]=%s\n", i, mConnections[i]);
   1179         }
   1180         pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants);
   1181         pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants);
   1182         pw.println(" mCallWaitingRegistrants=" + mCallWaitingRegistrants);
   1183         pw.println("droppedDuringPoll: size=" + mDroppedDuringPoll.size());
   1184         for(int i = 0; i < mDroppedDuringPoll.size(); i++) {
   1185             pw.printf( " mDroppedDuringPoll[%d]=%s\n", i, mDroppedDuringPoll.get(i));
   1186         }
   1187         pw.println(" mRingingCall=" + mRingingCall);
   1188         pw.println(" mForegroundCall=" + mForegroundCall);
   1189         pw.println(" mBackgroundCall=" + mBackgroundCall);
   1190         pw.println(" mPendingMO=" + mPendingMO);
   1191         pw.println(" mHangupPendingMO=" + mHangupPendingMO);
   1192         pw.println(" mPendingCallInEcm=" + mPendingCallInEcm);
   1193         pw.println(" mIsInEmergencyCall=" + mIsInEmergencyCall);
   1194         pw.println(" mPhone=" + mPhone);
   1195         pw.println(" mDesiredMute=" + mDesiredMute);
   1196         pw.println(" mPendingCallClirMode=" + mPendingCallClirMode);
   1197         pw.println(" mState=" + mState);
   1198         pw.println(" mIsEcmTimerCanceled=" + mIsEcmTimerCanceled);
   1199     }
   1200 }
   1201