1 /*
2  * Copyright (C) 2015 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;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.os.AsyncResult;
24 import android.os.Bundle;
25 import android.os.Handler;
26 import android.os.Message;
27 import android.os.Registrant;
28 import android.os.RegistrantList;
29 import android.os.SystemProperties;
30 import android.telephony.CellLocation;
31 import android.telephony.DisconnectCause;
32 import android.telephony.PhoneNumberUtils;
33 import android.telephony.ServiceState;
34 import android.telephony.TelephonyManager;
35 import android.telephony.cdma.CdmaCellLocation;
36 import android.telephony.gsm.GsmCellLocation;
37 import android.text.TextUtils;
38 import java.util.Iterator;
39 import android.telephony.Rlog;
40 import android.util.EventLog;
41 
42 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
43 import com.android.internal.telephony.EventLogTags;
44 
45 import java.io.FileDescriptor;
46 import java.io.PrintWriter;
47 import java.util.List;
48 import java.util.ArrayList;
49 
50 /**
51  * {@hide}
52  */
53 public class GsmCdmaCallTracker extends CallTracker {
54     private static final String LOG_TAG = "GsmCdmaCallTracker";
55     private static final boolean REPEAT_POLLING = false;
56 
57     private static final boolean DBG_POLL = false;
58     private static final boolean VDBG = false;
59 
60     //***** Constants
61 
62     public static final int MAX_CONNECTIONS_GSM = 19;   //7 allowed in GSM + 12 from IMS for SRVCC
63     private static final int MAX_CONNECTIONS_PER_CALL_GSM = 5; //only 5 connections allowed per call
64 
65     private static final int MAX_CONNECTIONS_CDMA = 8;
66     private static final int MAX_CONNECTIONS_PER_CALL_CDMA = 1; //only 1 connection allowed per call
67 
68     //***** Instance Variables
69     private GsmCdmaConnection mConnections[];
70     private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
71     private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
72 
73     // connections dropped during last poll
74     private ArrayList<GsmCdmaConnection> mDroppedDuringPoll =
75             new ArrayList<GsmCdmaConnection>(MAX_CONNECTIONS_GSM);
76 
77     public GsmCdmaCall mRingingCall = new GsmCdmaCall(this);
78     // A call that is ringing or (call) waiting
79     public GsmCdmaCall mForegroundCall = new GsmCdmaCall(this);
80     public GsmCdmaCall mBackgroundCall = new GsmCdmaCall(this);
81 
82     private GsmCdmaConnection mPendingMO;
83     private boolean mHangupPendingMO;
84 
85     private GsmCdmaPhone mPhone;
86 
87     private boolean mDesiredMute = false;    // false = mute off
88 
89     public PhoneConstants.State mState = PhoneConstants.State.IDLE;
90 
91     private TelephonyEventLog mEventLog;
92 
93     // Following member variables are for CDMA only
94     private RegistrantList mCallWaitingRegistrants = new RegistrantList();
95     private boolean mPendingCallInEcm;
96     private boolean mIsInEmergencyCall;
97     private int mPendingCallClirMode;
98     private boolean mIsEcmTimerCanceled;
99     private int m3WayCallFlashDelay;
100 
101     /**
102      * Listens for Emergency Callback Mode state change intents
103      */
104     private BroadcastReceiver mEcmExitReceiver = new BroadcastReceiver() {
105         @Override
106         public void onReceive(Context context, Intent intent) {
107             if (intent.getAction().equals(
108                     TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
109 
110                 boolean isInEcm = intent.getBooleanExtra(PhoneConstants.PHONE_IN_ECM_STATE, false);
111                 log("Received ACTION_EMERGENCY_CALLBACK_MODE_CHANGED isInEcm = " + isInEcm);
112 
113                 // If we exit ECM mode, notify all connections.
114                 if (!isInEcm) {
115                     // Although mConnections seems to be the place to look, it is not guaranteed
116                     // to have all of the connections we're tracking.  THe best place to look is in
117                     // the Call objects associated with the tracker.
118                     List<Connection> toNotify = new ArrayList<Connection>();
119                     toNotify.addAll(mRingingCall.getConnections());
120                     toNotify.addAll(mForegroundCall.getConnections());
121                     toNotify.addAll(mBackgroundCall.getConnections());
122                     if (mPendingMO != null) {
123                         toNotify.add(mPendingMO);
124                     }
125 
126                     // Notify connections that ECM mode exited.
127                     for (Connection connection : toNotify) {
128                         if (connection != null) {
129                             connection.onExitedEcmMode();
130                         }
131                     }
132                 }
133             }
134         }
135     };
136 
137     //***** Events
138 
139 
140     //***** Constructors
141 
GsmCdmaCallTracker(GsmCdmaPhone phone)142     public GsmCdmaCallTracker (GsmCdmaPhone phone) {
143         this.mPhone = phone;
144         mCi = phone.mCi;
145         mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
146         mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null);
147         mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);
148 
149         // Register receiver for ECM exit
150         IntentFilter filter = new IntentFilter();
151         filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
152         mPhone.getContext().registerReceiver(mEcmExitReceiver, filter);
153 
154         updatePhoneType(true);
155 
156         mEventLog = new TelephonyEventLog(mPhone.getPhoneId());
157     }
158 
updatePhoneType()159     public void updatePhoneType() {
160         updatePhoneType(false);
161     }
162 
updatePhoneType(boolean duringInit)163     private void updatePhoneType(boolean duringInit) {
164         if (!duringInit) {
165             reset();
166             pollCallsWhenSafe();
167         }
168         if (mPhone.isPhoneTypeGsm()) {
169             mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_GSM];
170             mCi.unregisterForCallWaitingInfo(this);
171         } else {
172             mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_CDMA];
173             mPendingCallInEcm = false;
174             mIsInEmergencyCall = false;
175             mPendingCallClirMode = CommandsInterface.CLIR_DEFAULT;
176             mIsEcmTimerCanceled = false;
177             m3WayCallFlashDelay = 0;
178             mCi.registerForCallWaitingInfo(this, EVENT_CALL_WAITING_INFO_CDMA, null);
179         }
180     }
181 
reset()182     private void reset() {
183         Rlog.d(LOG_TAG, "reset");
184 
185         clearDisconnected();
186 
187         for (GsmCdmaConnection gsmCdmaConnection : mConnections) {
188             if (gsmCdmaConnection != null) {
189                 gsmCdmaConnection.dispose();
190             }
191         }
192     }
193 
194     @Override
finalize()195     protected void finalize() {
196         Rlog.d(LOG_TAG, "GsmCdmaCallTracker finalized");
197     }
198 
199     //***** Instance Methods
200 
201     //***** Public Methods
202     @Override
registerForVoiceCallStarted(Handler h, int what, Object obj)203     public void registerForVoiceCallStarted(Handler h, int what, Object obj) {
204         Registrant r = new Registrant(h, what, obj);
205         mVoiceCallStartedRegistrants.add(r);
206         // Notify if in call when registering
207         if (mState != PhoneConstants.State.IDLE) {
208             r.notifyRegistrant(new AsyncResult(null, null, null));
209         }
210     }
211 
212     @Override
unregisterForVoiceCallStarted(Handler h)213     public void unregisterForVoiceCallStarted(Handler h) {
214         mVoiceCallStartedRegistrants.remove(h);
215     }
216 
217     @Override
registerForVoiceCallEnded(Handler h, int what, Object obj)218     public void registerForVoiceCallEnded(Handler h, int what, Object obj) {
219         Registrant r = new Registrant(h, what, obj);
220         mVoiceCallEndedRegistrants.add(r);
221     }
222 
223     @Override
unregisterForVoiceCallEnded(Handler h)224     public void unregisterForVoiceCallEnded(Handler h) {
225         mVoiceCallEndedRegistrants.remove(h);
226     }
227 
registerForCallWaiting(Handler h, int what, Object obj)228     public void registerForCallWaiting(Handler h, int what, Object obj) {
229         Registrant r = new Registrant (h, what, obj);
230         mCallWaitingRegistrants.add(r);
231     }
232 
unregisterForCallWaiting(Handler h)233     public void unregisterForCallWaiting(Handler h) {
234         mCallWaitingRegistrants.remove(h);
235     }
236 
fakeHoldForegroundBeforeDial()237     private void fakeHoldForegroundBeforeDial() {
238         List<Connection> connCopy;
239 
240         // We need to make a copy here, since fakeHoldBeforeDial()
241         // modifies the lists, and we don't want to reverse the order
242         connCopy = (List<Connection>) mForegroundCall.mConnections.clone();
243 
244         for (int i = 0, s = connCopy.size() ; i < s ; i++) {
245             GsmCdmaConnection conn = (GsmCdmaConnection)connCopy.get(i);
246 
247             conn.fakeHoldBeforeDial();
248         }
249     }
250 
251     //GSM
252     /**
253      * clirMode is one of the CLIR_ constants
254      */
dial(String dialString, int clirMode, UUSInfo uusInfo, Bundle intentExtras)255     public synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo,
256                                         Bundle intentExtras)
257             throws CallStateException {
258         // note that this triggers call state changed notif
259         clearDisconnected();
260 
261         if (!canDial()) {
262             throw new CallStateException("cannot dial in current state");
263         }
264 
265         String origNumber = dialString;
266         dialString = convertNumberIfNecessary(mPhone, dialString);
267 
268         // The new call must be assigned to the foreground call.
269         // That call must be idle, so place anything that's
270         // there on hold
271         if (mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE) {
272             // this will probably be done by the radio anyway
273             // but the dial might fail before this happens
274             // and we need to make sure the foreground call is clear
275             // for the newly dialed connection
276             switchWaitingOrHoldingAndActive();
277             // This is a hack to delay DIAL so that it is sent out to RIL only after
278             // EVENT_SWITCH_RESULT is received. We've seen failures when adding a new call to
279             // multi-way conference calls due to DIAL being sent out before SWITCH is processed
280             try {
281                 Thread.sleep(500);
282             } catch (InterruptedException e) {
283                 // do nothing
284             }
285 
286             // Fake local state so that
287             // a) foregroundCall is empty for the newly dialed connection
288             // b) hasNonHangupStateChanged remains false in the
289             // next poll, so that we don't clear a failed dialing call
290             fakeHoldForegroundBeforeDial();
291         }
292 
293         if (mForegroundCall.getState() != GsmCdmaCall.State.IDLE) {
294             //we should have failed in !canDial() above before we get here
295             throw new CallStateException("cannot dial in current state");
296         }
297 
298         mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString),
299                 this, mForegroundCall);
300         mHangupPendingMO = false;
301 
302         if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
303                 || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) {
304             // Phone number is invalid
305             mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;
306 
307             // handlePollCalls() will notice this call not present
308             // and will mark it as dropped.
309             pollCallsWhenSafe();
310         } else {
311             // Always unmute when initiating a new call
312             setMute(false);
313 
314             mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage());
315         }
316 
317         if (mNumberConverted) {
318             mPendingMO.setConverted(origNumber);
319             mNumberConverted = false;
320         }
321 
322         updatePhoneState();
323         mPhone.notifyPreciseCallStateChanged();
324 
325         return mPendingMO;
326     }
327 
328     //CDMA
329     /**
330      * Handle Ecm timer to be canceled or re-started
331      */
handleEcmTimer(int action)332     private void handleEcmTimer(int action) {
333         mPhone.handleTimerInEmergencyCallbackMode(action);
334         switch(action) {
335             case GsmCdmaPhone.CANCEL_ECM_TIMER: mIsEcmTimerCanceled = true; break;
336             case GsmCdmaPhone.RESTART_ECM_TIMER: mIsEcmTimerCanceled = false; break;
337             default:
338                 Rlog.e(LOG_TAG, "handleEcmTimer, unsupported action " + action);
339         }
340     }
341 
342     //CDMA
343     /**
344      * Disable data call when emergency call is connected
345      */
disableDataCallInEmergencyCall(String dialString)346     private void disableDataCallInEmergencyCall(String dialString) {
347         if (PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString)) {
348             if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall");
349             setIsInEmergencyCall();
350         }
351     }
352 
353     //CDMA
setIsInEmergencyCall()354     public void setIsInEmergencyCall() {
355         mIsInEmergencyCall = true;
356         mPhone.mDcTracker.setInternalDataEnabled(false);
357         mPhone.notifyEmergencyCallRegistrants(true);
358         mPhone.sendEmergencyCallStateChange(true);
359     }
360 
361     //CDMA
362     /**
363      * clirMode is one of the CLIR_ constants
364      */
dial(String dialString, int clirMode)365     private Connection dial(String dialString, int clirMode) throws CallStateException {
366         // note that this triggers call state changed notif
367         clearDisconnected();
368 
369         if (!canDial()) {
370             throw new CallStateException("cannot dial in current state");
371         }
372 
373         TelephonyManager tm =
374                 (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
375         String origNumber = dialString;
376         String operatorIsoContry = tm.getNetworkCountryIsoForPhone(mPhone.getPhoneId());
377         String simIsoContry = tm.getSimCountryIsoForPhone(mPhone.getPhoneId());
378         boolean internationalRoaming = !TextUtils.isEmpty(operatorIsoContry)
379                 && !TextUtils.isEmpty(simIsoContry)
380                 && !simIsoContry.equals(operatorIsoContry);
381         if (internationalRoaming) {
382             if ("us".equals(simIsoContry)) {
383                 internationalRoaming = internationalRoaming && !"vi".equals(operatorIsoContry);
384             } else if ("vi".equals(simIsoContry)) {
385                 internationalRoaming = internationalRoaming && !"us".equals(operatorIsoContry);
386             }
387         }
388         if (internationalRoaming) {
389             dialString = convertNumberIfNecessary(mPhone, dialString);
390         }
391 
392         String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
393         boolean isPhoneInEcmMode = inEcm.equals("true");
394         boolean isEmergencyCall =
395                 PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString);
396 
397         // Cancel Ecm timer if a second emergency call is originating in Ecm mode
398         if (isPhoneInEcmMode && isEmergencyCall) {
399             handleEcmTimer(GsmCdmaPhone.CANCEL_ECM_TIMER);
400         }
401 
402         // The new call must be assigned to the foreground call.
403         // That call must be idle, so place anything that's
404         // there on hold
405         if (mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE) {
406             return dialThreeWay(dialString);
407         }
408 
409         mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString),
410                 this, mForegroundCall);
411         mHangupPendingMO = false;
412 
413         if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
414                 || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0 ) {
415             // Phone number is invalid
416             mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;
417 
418             // handlePollCalls() will notice this call not present
419             // and will mark it as dropped.
420             pollCallsWhenSafe();
421         } else {
422             // Always unmute when initiating a new call
423             setMute(false);
424 
425             // Check data call
426             disableDataCallInEmergencyCall(dialString);
427 
428             // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
429             if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
430                 mCi.dial(mPendingMO.getAddress(), clirMode, obtainCompleteMessage());
431             } else {
432                 mPhone.exitEmergencyCallbackMode();
433                 mPhone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null);
434                 mPendingCallClirMode=clirMode;
435                 mPendingCallInEcm=true;
436             }
437         }
438 
439         if (mNumberConverted) {
440             mPendingMO.setConverted(origNumber);
441             mNumberConverted = false;
442         }
443 
444         updatePhoneState();
445         mPhone.notifyPreciseCallStateChanged();
446 
447         return mPendingMO;
448     }
449 
450     //CDMA
dialThreeWay(String dialString)451     private Connection dialThreeWay(String dialString) {
452         if (!mForegroundCall.isIdle()) {
453             // Check data call
454             disableDataCallInEmergencyCall(dialString);
455 
456             // Attach the new connection to foregroundCall
457             mPendingMO = new GsmCdmaConnection(mPhone,
458                     checkForTestEmergencyNumber(dialString), this, mForegroundCall);
459             // Some network need a empty flash before sending the normal one
460             m3WayCallFlashDelay = mPhone.getContext().getResources()
461                     .getInteger(com.android.internal.R.integer.config_cdma_3waycall_flash_delay);
462             if (m3WayCallFlashDelay > 0) {
463                 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_THREE_WAY_DIAL_BLANK_FLASH));
464             } else {
465                 mCi.sendCDMAFeatureCode(mPendingMO.getAddress(),
466                         obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA));
467             }
468             return mPendingMO;
469         }
470         return null;
471     }
472 
dial(String dialString)473     public Connection dial(String dialString) throws CallStateException {
474         if (isPhoneTypeGsm()) {
475             return dial(dialString, CommandsInterface.CLIR_DEFAULT, null);
476         } else {
477             return dial(dialString, CommandsInterface.CLIR_DEFAULT);
478         }
479     }
480 
481     //GSM
dial(String dialString, UUSInfo uusInfo, Bundle intentExtras)482     public Connection dial(String dialString, UUSInfo uusInfo, Bundle intentExtras)
483             throws CallStateException {
484         return dial(dialString, CommandsInterface.CLIR_DEFAULT, uusInfo, intentExtras);
485     }
486 
487     //GSM
dial(String dialString, int clirMode, Bundle intentExtras)488     private Connection dial(String dialString, int clirMode, Bundle intentExtras)
489             throws CallStateException {
490         return dial(dialString, clirMode, null, intentExtras);
491     }
492 
acceptCall()493     public void acceptCall() throws CallStateException {
494         // FIXME if SWITCH fails, should retry with ANSWER
495         // in case the active/holding call disappeared and this
496         // is no longer call waiting
497 
498         if (mRingingCall.getState() == GsmCdmaCall.State.INCOMING) {
499             Rlog.i("phone", "acceptCall: incoming...");
500             // Always unmute when answering a new call
501             setMute(false);
502             mCi.acceptCall(obtainCompleteMessage());
503         } else if (mRingingCall.getState() == GsmCdmaCall.State.WAITING) {
504             if (isPhoneTypeGsm()) {
505                 setMute(false);
506             } else {
507                 GsmCdmaConnection cwConn = (GsmCdmaConnection)(mRingingCall.getLatestConnection());
508 
509                 // Since there is no network response for supplimentary
510                 // service for CDMA, we assume call waiting is answered.
511                 // ringing Call state change to idle is in GsmCdmaCall.detach
512                 // triggered by updateParent.
513                 cwConn.updateParent(mRingingCall, mForegroundCall);
514                 cwConn.onConnectedInOrOut();
515                 updatePhoneState();
516             }
517             switchWaitingOrHoldingAndActive();
518         } else {
519             throw new CallStateException("phone not ringing");
520         }
521     }
522 
rejectCall()523     public void rejectCall() throws CallStateException {
524         // AT+CHLD=0 means "release held or UDUB"
525         // so if the phone isn't ringing, this could hang up held
526         if (mRingingCall.getState().isRinging()) {
527             mCi.rejectCall(obtainCompleteMessage());
528         } else {
529             throw new CallStateException("phone not ringing");
530         }
531     }
532 
533     //CDMA
flashAndSetGenericTrue()534     private void flashAndSetGenericTrue() {
535         mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT));
536 
537         mPhone.notifyPreciseCallStateChanged();
538     }
539 
switchWaitingOrHoldingAndActive()540     public void switchWaitingOrHoldingAndActive() throws CallStateException {
541         // Should we bother with this check?
542         if (mRingingCall.getState() == GsmCdmaCall.State.INCOMING) {
543             throw new CallStateException("cannot be in the incoming state");
544         } else {
545             if (isPhoneTypeGsm()) {
546                 mCi.switchWaitingOrHoldingAndActive(
547                         obtainCompleteMessage(EVENT_SWITCH_RESULT));
548             } else {
549                 if (mForegroundCall.getConnections().size() > 1) {
550                     flashAndSetGenericTrue();
551                 } else {
552                     // Send a flash command to CDMA network for putting the other party on hold.
553                     // For CDMA networks which do not support this the user would just hear a beep
554                     // from the network. For CDMA networks which do support it will put the other
555                     // party on hold.
556                     mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT));
557                 }
558             }
559         }
560     }
561 
conference()562     public void conference() {
563         if (isPhoneTypeGsm()) {
564             mCi.conference(obtainCompleteMessage(EVENT_CONFERENCE_RESULT));
565         } else {
566             // Should we be checking state?
567             flashAndSetGenericTrue();
568         }
569     }
570 
explicitCallTransfer()571     public void explicitCallTransfer() {
572         mCi.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT));
573     }
574 
clearDisconnected()575     public void clearDisconnected() {
576         internalClearDisconnected();
577 
578         updatePhoneState();
579         mPhone.notifyPreciseCallStateChanged();
580     }
581 
canConference()582     public boolean canConference() {
583         return mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE
584                 && mBackgroundCall.getState() == GsmCdmaCall.State.HOLDING
585                 && !mBackgroundCall.isFull()
586                 && !mForegroundCall.isFull();
587     }
588 
canDial()589     private boolean canDial() {
590         boolean ret;
591         int serviceState = mPhone.getServiceState().getState();
592         String disableCall = SystemProperties.get(
593                 TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
594 
595         ret = (serviceState != ServiceState.STATE_POWER_OFF)
596                 && mPendingMO == null
597                 && !mRingingCall.isRinging()
598                 && !disableCall.equals("true")
599                 && (!mForegroundCall.getState().isAlive()
600                     || !mBackgroundCall.getState().isAlive()
601                     || (!isPhoneTypeGsm()
602                         && mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE));
603 
604         if (!ret) {
605             log(String.format("canDial is false\n" +
606                             "((serviceState=%d) != ServiceState.STATE_POWER_OFF)::=%s\n" +
607                             "&& pendingMO == null::=%s\n" +
608                             "&& !ringingCall.isRinging()::=%s\n" +
609                             "&& !disableCall.equals(\"true\")::=%s\n" +
610                             "&& (!foregroundCall.getState().isAlive()::=%s\n" +
611                             "   || foregroundCall.getState() == GsmCdmaCall.State.ACTIVE::=%s\n" +
612                             "   ||!backgroundCall.getState().isAlive())::=%s)",
613                     serviceState,
614                     serviceState != ServiceState.STATE_POWER_OFF,
615                     mPendingMO == null,
616                     !mRingingCall.isRinging(),
617                     !disableCall.equals("true"),
618                     !mForegroundCall.getState().isAlive(),
619                     mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE,
620                     !mBackgroundCall.getState().isAlive()));
621         }
622 
623         return ret;
624     }
625 
canTransfer()626     public boolean canTransfer() {
627         if (isPhoneTypeGsm()) {
628             return (mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE
629                     || mForegroundCall.getState() == GsmCdmaCall.State.ALERTING
630                     || mForegroundCall.getState() == GsmCdmaCall.State.DIALING)
631                     && mBackgroundCall.getState() == GsmCdmaCall.State.HOLDING;
632         } else {
633             Rlog.e(LOG_TAG, "canTransfer: not possible in CDMA");
634             return false;
635         }
636     }
637 
638     //***** Private Instance Methods
639 
internalClearDisconnected()640     private void internalClearDisconnected() {
641         mRingingCall.clearDisconnected();
642         mForegroundCall.clearDisconnected();
643         mBackgroundCall.clearDisconnected();
644     }
645 
646     /**
647      * Obtain a message to use for signalling "invoke getCurrentCalls() when
648      * this operation and all other pending operations are complete
649      */
obtainCompleteMessage()650     private Message obtainCompleteMessage() {
651         return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
652     }
653 
654     /**
655      * Obtain a message to use for signalling "invoke getCurrentCalls() when
656      * this operation and all other pending operations are complete
657      */
obtainCompleteMessage(int what)658     private Message obtainCompleteMessage(int what) {
659         mPendingOperations++;
660         mLastRelevantPoll = null;
661         mNeedsPoll = true;
662 
663         if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" +
664                 mPendingOperations + ", needsPoll=" + mNeedsPoll);
665 
666         return obtainMessage(what);
667     }
668 
operationComplete()669     private void operationComplete() {
670         mPendingOperations--;
671 
672         if (DBG_POLL) log("operationComplete: pendingOperations=" +
673                 mPendingOperations + ", needsPoll=" + mNeedsPoll);
674 
675         if (mPendingOperations == 0 && mNeedsPoll) {
676             mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
677             mCi.getCurrentCalls(mLastRelevantPoll);
678         } else if (mPendingOperations < 0) {
679             // this should never happen
680             Rlog.e(LOG_TAG,"GsmCdmaCallTracker.pendingOperations < 0");
681             mPendingOperations = 0;
682         }
683     }
684 
updatePhoneState()685     private void updatePhoneState() {
686         PhoneConstants.State oldState = mState;
687         if (mRingingCall.isRinging()) {
688             mState = PhoneConstants.State.RINGING;
689         } else if (mPendingMO != null ||
690                 !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
691             mState = PhoneConstants.State.OFFHOOK;
692         } else {
693             Phone imsPhone = mPhone.getImsPhone();
694             if ( mState == PhoneConstants.State.OFFHOOK && (imsPhone != null)){
695                 imsPhone.callEndCleanupHandOverCallIfAny();
696             }
697             mState = PhoneConstants.State.IDLE;
698         }
699 
700         if (mState == PhoneConstants.State.IDLE && oldState != mState) {
701             mVoiceCallEndedRegistrants.notifyRegistrants(
702                 new AsyncResult(null, null, null));
703         } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
704             mVoiceCallStartedRegistrants.notifyRegistrants (
705                     new AsyncResult(null, null, null));
706         }
707         if (Phone.DEBUG_PHONE) {
708             log("update phone state, old=" + oldState + " new="+ mState);
709         }
710         if (mState != oldState) {
711             mPhone.notifyPhoneStateChanged();
712             mEventLog.writePhoneState(mState);
713         }
714     }
715 
716     // ***** Overwritten from CallTracker
717 
718     @Override
handlePollCalls(AsyncResult ar)719     protected synchronized void handlePollCalls(AsyncResult ar) {
720         List polledCalls;
721 
722         if (VDBG) log("handlePollCalls");
723         if (ar.exception == null) {
724             polledCalls = (List)ar.result;
725         } else if (isCommandExceptionRadioNotAvailable(ar.exception)) {
726             // just a dummy empty ArrayList to cause the loop
727             // to hang up all the calls
728             polledCalls = new ArrayList();
729         } else {
730             // Radio probably wasn't ready--try again in a bit
731             // But don't keep polling if the channel is closed
732             pollCallsAfterDelay();
733             return;
734         }
735 
736         Connection newRinging = null; //or waiting
737         ArrayList<Connection> newUnknownConnectionsGsm = new ArrayList<Connection>();
738         Connection newUnknownConnectionCdma = null;
739         boolean hasNonHangupStateChanged = false;   // Any change besides
740                                                     // a dropped connection
741         boolean hasAnyCallDisconnected = false;
742         boolean needsPollDelay = false;
743         boolean unknownConnectionAppeared = false;
744 
745         //CDMA
746         boolean noConnectionExists = true;
747 
748         for (int i = 0, curDC = 0, dcSize = polledCalls.size()
749                 ; i < mConnections.length; i++) {
750             GsmCdmaConnection conn = mConnections[i];
751             DriverCall dc = null;
752 
753             // polledCall list is sparse
754             if (curDC < dcSize) {
755                 dc = (DriverCall) polledCalls.get(curDC);
756 
757                 if (dc.index == i+1) {
758                     curDC++;
759                 } else {
760                     dc = null;
761                 }
762             }
763 
764             //CDMA
765             if (conn != null || dc != null) {
766                 noConnectionExists = false;
767             }
768 
769             if (DBG_POLL) log("poll: conn[i=" + i + "]=" +
770                     conn+", dc=" + dc);
771 
772             if (conn == null && dc != null) {
773                 // Connection appeared in CLCC response that we don't know about
774                 if (mPendingMO != null && mPendingMO.compareTo(dc)) {
775 
776                     if (DBG_POLL) log("poll: pendingMO=" + mPendingMO);
777 
778                     // It's our pending mobile originating call
779                     mConnections[i] = mPendingMO;
780                     mPendingMO.mIndex = i;
781                     mPendingMO.update(dc);
782                     mPendingMO = null;
783 
784                     // Someone has already asked to hangup this call
785                     if (mHangupPendingMO) {
786                         mHangupPendingMO = false;
787 
788                         // Re-start Ecm timer when an uncompleted emergency call ends
789                         if (!isPhoneTypeGsm() && mIsEcmTimerCanceled) {
790                             handleEcmTimer(GsmCdmaPhone.RESTART_ECM_TIMER);
791                         }
792 
793                         try {
794                             if (Phone.DEBUG_PHONE) log(
795                                     "poll: hangupPendingMO, hangup conn " + i);
796                             hangup(mConnections[i]);
797                         } catch (CallStateException ex) {
798                             Rlog.e(LOG_TAG, "unexpected error on hangup");
799                         }
800 
801                         // Do not continue processing this poll
802                         // Wait for hangup and repoll
803                         return;
804                     }
805                 } else {
806                     if (Phone.DEBUG_PHONE) {
807                         log("pendingMo=" + mPendingMO + ", dc=" + dc);
808                     }
809 
810                     mConnections[i] = new GsmCdmaConnection(mPhone, dc, this, i);
811 
812                     Connection hoConnection = getHoConnection(dc);
813                     if (hoConnection != null) {
814                         // Single Radio Voice Call Continuity (SRVCC) completed
815                         mConnections[i].migrateFrom(hoConnection);
816                         // Updating connect time for silent redial cases (ex: Calls are transferred
817                         // from DIALING/ALERTING/INCOMING/WAITING to ACTIVE)
818                         if (hoConnection.mPreHandoverState != GsmCdmaCall.State.ACTIVE &&
819                                 hoConnection.mPreHandoverState != GsmCdmaCall.State.HOLDING &&
820                                 dc.state == DriverCall.State.ACTIVE) {
821                             mConnections[i].onConnectedInOrOut();
822                         }
823 
824                         mHandoverConnections.remove(hoConnection);
825 
826                         if (isPhoneTypeGsm()) {
827                             for (Iterator<Connection> it = mHandoverConnections.iterator();
828                                  it.hasNext(); ) {
829                                 Connection c = it.next();
830                                 Rlog.i(LOG_TAG, "HO Conn state is " + c.mPreHandoverState);
831                                 if (c.mPreHandoverState == mConnections[i].getState()) {
832                                     Rlog.i(LOG_TAG, "Removing HO conn "
833                                             + hoConnection + c.mPreHandoverState);
834                                     it.remove();
835                                 }
836                             }
837                         }
838 
839                         mPhone.notifyHandoverStateChanged(mConnections[i]);
840                     } else {
841                         // find if the MT call is a new ring or unknown connection
842                         newRinging = checkMtFindNewRinging(dc,i);
843                         if (newRinging == null) {
844                             unknownConnectionAppeared = true;
845                             if (isPhoneTypeGsm()) {
846                                 newUnknownConnectionsGsm.add(mConnections[i]);
847                             } else {
848                                 newUnknownConnectionCdma = mConnections[i];
849                             }
850                         }
851                     }
852                 }
853                 hasNonHangupStateChanged = true;
854             } else if (conn != null && dc == null) {
855                 if (isPhoneTypeGsm()) {
856                     // Connection missing in CLCC response that we were
857                     // tracking.
858                     mDroppedDuringPoll.add(conn);
859                     // Dropped connections are removed from the CallTracker
860                     // list but kept in the GsmCdmaCall list
861                     mConnections[i] = null;
862                 } else {
863                     // This case means the RIL has no more active call anymore and
864                     // we need to clean up the foregroundCall and ringingCall.
865                     // Loop through foreground call connections as
866                     // it contains the known logical connections.
867                     int count = mForegroundCall.mConnections.size();
868                     for (int n = 0; n < count; n++) {
869                         if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll");
870                         GsmCdmaConnection cn = (GsmCdmaConnection)mForegroundCall.mConnections.get(n);
871                         mDroppedDuringPoll.add(cn);
872                     }
873                     count = mRingingCall.mConnections.size();
874                     // Loop through ringing call connections as
875                     // it may contain the known logical connections.
876                     for (int n = 0; n < count; n++) {
877                         if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll");
878                         GsmCdmaConnection cn = (GsmCdmaConnection)mRingingCall.mConnections.get(n);
879                         mDroppedDuringPoll.add(cn);
880                     }
881 
882                     // Re-start Ecm timer when the connected emergency call ends
883                     if (mIsEcmTimerCanceled) {
884                         handleEcmTimer(GsmCdmaPhone.RESTART_ECM_TIMER);
885                     }
886                     // If emergency call is not going through while dialing
887                     checkAndEnableDataCallAfterEmergencyCallDropped();
888 
889                     // Dropped connections are removed from the CallTracker
890                     // list but kept in the Call list
891                     mConnections[i] = null;
892 
893                 }
894             } else if (conn != null && dc != null && !conn.compareTo(dc) && isPhoneTypeGsm()) {
895                 // Connection in CLCC response does not match what
896                 // we were tracking. Assume dropped call and new call
897 
898                 mDroppedDuringPoll.add(conn);
899                 mConnections[i] = new GsmCdmaConnection (mPhone, dc, this, i);
900 
901                 if (mConnections[i].getCall() == mRingingCall) {
902                     newRinging = mConnections[i];
903                 } // else something strange happened
904                 hasNonHangupStateChanged = true;
905             } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
906                 // Call collision case
907                 if (!isPhoneTypeGsm() && conn.isIncoming() != dc.isMT) {
908                     if (dc.isMT == true) {
909                         // Mt call takes precedence than Mo,drops Mo
910                         mDroppedDuringPoll.add(conn);
911                         // find if the MT call is a new ring or unknown connection
912                         newRinging = checkMtFindNewRinging(dc,i);
913                         if (newRinging == null) {
914                             unknownConnectionAppeared = true;
915                             newUnknownConnectionCdma = conn;
916                         }
917                         checkAndEnableDataCallAfterEmergencyCallDropped();
918                     } else {
919                         // Call info stored in conn is not consistent with the call info from dc.
920                         // We should follow the rule of MT calls taking precedence over MO calls
921                         // when there is conflict, so here we drop the call info from dc and
922                         // continue to use the call info from conn, and only take a log.
923                         Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc);
924                     }
925                 } else {
926                     boolean changed;
927                     changed = conn.update(dc);
928                     hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
929                 }
930             }
931 
932             if (REPEAT_POLLING) {
933                 if (dc != null) {
934                     // FIXME with RIL, we should not need this anymore
935                     if ((dc.state == DriverCall.State.DIALING
936                             /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/)
937                         || (dc.state == DriverCall.State.ALERTING
938                             /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/)
939                         || (dc.state == DriverCall.State.INCOMING
940                             /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/)
941                         || (dc.state == DriverCall.State.WAITING
942                             /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)) {
943                         // Sometimes there's no unsolicited notification
944                         // for state transitions
945                         needsPollDelay = true;
946                     }
947                 }
948             }
949         }
950 
951         // Safety check so that obj is not stuck with mIsInEmergencyCall set to true (and data
952         // disabled). This should never happen though.
953         if (!isPhoneTypeGsm() && noConnectionExists) {
954             checkAndEnableDataCallAfterEmergencyCallDropped();
955         }
956 
957         // This is the first poll after an ATD.
958         // We expect the pending call to appear in the list
959         // If it does not, we land here
960         if (mPendingMO != null) {
961             Rlog.d(LOG_TAG, "Pending MO dropped before poll fg state:"
962                     + mForegroundCall.getState());
963 
964             mDroppedDuringPoll.add(mPendingMO);
965             mPendingMO = null;
966             mHangupPendingMO = false;
967 
968             if (!isPhoneTypeGsm()) {
969                 if( mPendingCallInEcm) {
970                     mPendingCallInEcm = false;
971                 }
972                 checkAndEnableDataCallAfterEmergencyCallDropped();
973             }
974         }
975 
976         if (newRinging != null) {
977             mPhone.notifyNewRingingConnection(newRinging);
978         }
979 
980         // clear the "local hangup" and "missed/rejected call"
981         // cases from the "dropped during poll" list
982         // These cases need no "last call fail" reason
983         for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
984             GsmCdmaConnection conn = mDroppedDuringPoll.get(i);
985             //CDMA
986             boolean wasDisconnected = false;
987 
988             if (conn.isIncoming() && conn.getConnectTime() == 0) {
989                 // Missed or rejected call
990                 int cause;
991                 if (conn.mCause == DisconnectCause.LOCAL) {
992                     cause = DisconnectCause.INCOMING_REJECTED;
993                 } else {
994                     cause = DisconnectCause.INCOMING_MISSED;
995                 }
996 
997                 if (Phone.DEBUG_PHONE) {
998                     log("missed/rejected call, conn.cause=" + conn.mCause);
999                     log("setting cause to " + cause);
1000                 }
1001                 mDroppedDuringPoll.remove(i);
1002                 hasAnyCallDisconnected |= conn.onDisconnect(cause);
1003                 wasDisconnected = true;
1004             } else if (conn.mCause == DisconnectCause.LOCAL
1005                     || conn.mCause == DisconnectCause.INVALID_NUMBER) {
1006                 mDroppedDuringPoll.remove(i);
1007                 hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
1008                 wasDisconnected = true;
1009             }
1010 
1011             if (!isPhoneTypeGsm() && wasDisconnected && unknownConnectionAppeared
1012                     && conn == newUnknownConnectionCdma) {
1013                 unknownConnectionAppeared = false;
1014                 newUnknownConnectionCdma = null;
1015             }
1016         }
1017 
1018         /* Disconnect any pending Handover connections */
1019         for (Iterator<Connection> it = mHandoverConnections.iterator();
1020                 it.hasNext();) {
1021             Connection hoConnection = it.next();
1022             log("handlePollCalls - disconnect hoConn= " + hoConnection +
1023                     " hoConn.State= " + hoConnection.getState());
1024             if (hoConnection.getState().isRinging()) {
1025                 hoConnection.onDisconnect(DisconnectCause.INCOMING_MISSED);
1026             } else {
1027                 hoConnection.onDisconnect(DisconnectCause.NOT_VALID);
1028             }
1029             it.remove();
1030         }
1031 
1032         // Any non-local disconnects: determine cause
1033         if (mDroppedDuringPoll.size() > 0) {
1034             mCi.getLastCallFailCause(
1035                 obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
1036         }
1037 
1038         if (needsPollDelay) {
1039             pollCallsAfterDelay();
1040         }
1041 
1042         // Cases when we can no longer keep disconnected Connection's
1043         // with their previous calls
1044         // 1) the phone has started to ring
1045         // 2) A Call/Connection object has changed state...
1046         //    we may have switched or held or answered (but not hung up)
1047         if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) {
1048             internalClearDisconnected();
1049         }
1050 
1051         if (VDBG) log("handlePollCalls calling updatePhoneState()");
1052         updatePhoneState();
1053 
1054         if (unknownConnectionAppeared) {
1055             if (isPhoneTypeGsm()) {
1056                 for (Connection c : newUnknownConnectionsGsm) {
1057                     log("Notify unknown for " + c);
1058                     mPhone.notifyUnknownConnection(c);
1059                 }
1060             } else {
1061                 mPhone.notifyUnknownConnection(newUnknownConnectionCdma);
1062             }
1063         }
1064 
1065         if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
1066             mPhone.notifyPreciseCallStateChanged();
1067         }
1068 
1069         //dumpState();
1070     }
1071 
handleRadioNotAvailable()1072     private void handleRadioNotAvailable() {
1073         // handlePollCalls will clear out its
1074         // call list when it gets the CommandException
1075         // error result from this
1076         pollCallsWhenSafe();
1077     }
1078 
dumpState()1079     private void dumpState() {
1080         List l;
1081 
1082         Rlog.i(LOG_TAG,"Phone State:" + mState);
1083 
1084         Rlog.i(LOG_TAG,"Ringing call: " + mRingingCall.toString());
1085 
1086         l = mRingingCall.getConnections();
1087         for (int i = 0, s = l.size(); i < s; i++) {
1088             Rlog.i(LOG_TAG,l.get(i).toString());
1089         }
1090 
1091         Rlog.i(LOG_TAG,"Foreground call: " + mForegroundCall.toString());
1092 
1093         l = mForegroundCall.getConnections();
1094         for (int i = 0, s = l.size(); i < s; i++) {
1095             Rlog.i(LOG_TAG,l.get(i).toString());
1096         }
1097 
1098         Rlog.i(LOG_TAG,"Background call: " + mBackgroundCall.toString());
1099 
1100         l = mBackgroundCall.getConnections();
1101         for (int i = 0, s = l.size(); i < s; i++) {
1102             Rlog.i(LOG_TAG,l.get(i).toString());
1103         }
1104 
1105     }
1106 
1107     //***** Called from GsmCdmaConnection
1108 
hangup(GsmCdmaConnection conn)1109     public void hangup(GsmCdmaConnection conn) throws CallStateException {
1110         if (conn.mOwner != this) {
1111             throw new CallStateException ("GsmCdmaConnection " + conn
1112                                     + "does not belong to GsmCdmaCallTracker " + this);
1113         }
1114 
1115         if (conn == mPendingMO) {
1116             // We're hanging up an outgoing call that doesn't have it's
1117             // GsmCdma index assigned yet
1118 
1119             if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true");
1120             mHangupPendingMO = true;
1121         } else if (!isPhoneTypeGsm()
1122                 && conn.getCall() == mRingingCall
1123                 && mRingingCall.getState() == GsmCdmaCall.State.WAITING) {
1124             // Handle call waiting hang up case.
1125             //
1126             // The ringingCall state will change to IDLE in GsmCdmaCall.detach
1127             // if the ringing call connection size is 0. We don't specifically
1128             // set the ringing call state to IDLE here to avoid a race condition
1129             // where a new call waiting could get a hang up from an old call
1130             // waiting ringingCall.
1131             //
1132             // PhoneApp does the call log itself since only PhoneApp knows
1133             // the hangup reason is user ignoring or timing out. So conn.onDisconnect()
1134             // is not called here. Instead, conn.onLocalDisconnect() is called.
1135             conn.onLocalDisconnect();
1136 
1137             updatePhoneState();
1138             mPhone.notifyPreciseCallStateChanged();
1139             return;
1140         } else {
1141             try {
1142                 mCi.hangupConnection (conn.getGsmCdmaIndex(), obtainCompleteMessage());
1143             } catch (CallStateException ex) {
1144                 // Ignore "connection not found"
1145                 // Call may have hung up already
1146                 Rlog.w(LOG_TAG,"GsmCdmaCallTracker WARN: hangup() on absent connection "
1147                                 + conn);
1148             }
1149         }
1150 
1151         conn.onHangupLocal();
1152     }
1153 
separate(GsmCdmaConnection conn)1154     public void separate(GsmCdmaConnection conn) throws CallStateException {
1155         if (conn.mOwner != this) {
1156             throw new CallStateException ("GsmCdmaConnection " + conn
1157                                     + "does not belong to GsmCdmaCallTracker " + this);
1158         }
1159         try {
1160             mCi.separateConnection (conn.getGsmCdmaIndex(),
1161                 obtainCompleteMessage(EVENT_SEPARATE_RESULT));
1162         } catch (CallStateException ex) {
1163             // Ignore "connection not found"
1164             // Call may have hung up already
1165             Rlog.w(LOG_TAG,"GsmCdmaCallTracker WARN: separate() on absent connection " + conn);
1166         }
1167     }
1168 
1169     //***** Called from GsmCdmaPhone
1170 
setMute(boolean mute)1171     public void setMute(boolean mute) {
1172         mDesiredMute = mute;
1173         mCi.setMute(mDesiredMute, null);
1174     }
1175 
getMute()1176     public boolean getMute() {
1177         return mDesiredMute;
1178     }
1179 
1180 
1181     //***** Called from GsmCdmaCall
1182 
hangup(GsmCdmaCall call)1183     public void hangup(GsmCdmaCall call) throws CallStateException {
1184         if (call.getConnections().size() == 0) {
1185             throw new CallStateException("no connections in call");
1186         }
1187 
1188         if (call == mRingingCall) {
1189             if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background");
1190             mCi.hangupWaitingOrBackground(obtainCompleteMessage());
1191         } else if (call == mForegroundCall) {
1192             if (call.isDialingOrAlerting()) {
1193                 if (Phone.DEBUG_PHONE) {
1194                     log("(foregnd) hangup dialing or alerting...");
1195                 }
1196                 hangup((GsmCdmaConnection)(call.getConnections().get(0)));
1197             } else if (isPhoneTypeGsm()
1198                     && mRingingCall.isRinging()) {
1199                 // Do not auto-answer ringing on CHUP, instead just end active calls
1200                 log("hangup all conns in active/background call, without affecting ringing call");
1201                 hangupAllConnections(call);
1202             } else {
1203                 hangupForegroundResumeBackground();
1204             }
1205         } else if (call == mBackgroundCall) {
1206             if (mRingingCall.isRinging()) {
1207                 if (Phone.DEBUG_PHONE) {
1208                     log("hangup all conns in background call");
1209                 }
1210                 hangupAllConnections(call);
1211             } else {
1212                 hangupWaitingOrBackground();
1213             }
1214         } else {
1215             throw new RuntimeException ("GsmCdmaCall " + call +
1216                     "does not belong to GsmCdmaCallTracker " + this);
1217         }
1218 
1219         call.onHangupLocal();
1220         mPhone.notifyPreciseCallStateChanged();
1221     }
1222 
hangupWaitingOrBackground()1223     public void hangupWaitingOrBackground() {
1224         if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground");
1225         mCi.hangupWaitingOrBackground(obtainCompleteMessage());
1226     }
1227 
hangupForegroundResumeBackground()1228     public void hangupForegroundResumeBackground() {
1229         if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground");
1230         mCi.hangupForegroundResumeBackground(obtainCompleteMessage());
1231     }
1232 
hangupConnectionByIndex(GsmCdmaCall call, int index)1233     public void hangupConnectionByIndex(GsmCdmaCall call, int index)
1234             throws CallStateException {
1235         int count = call.mConnections.size();
1236         for (int i = 0; i < count; i++) {
1237             GsmCdmaConnection cn = (GsmCdmaConnection)call.mConnections.get(i);
1238             if (cn.getGsmCdmaIndex() == index) {
1239                 mCi.hangupConnection(index, obtainCompleteMessage());
1240                 return;
1241             }
1242         }
1243 
1244         throw new CallStateException("no GsmCdma index found");
1245     }
1246 
hangupAllConnections(GsmCdmaCall call)1247     public void hangupAllConnections(GsmCdmaCall call) {
1248         try {
1249             int count = call.mConnections.size();
1250             for (int i = 0; i < count; i++) {
1251                 GsmCdmaConnection cn = (GsmCdmaConnection)call.mConnections.get(i);
1252                 mCi.hangupConnection(cn.getGsmCdmaIndex(), obtainCompleteMessage());
1253             }
1254         } catch (CallStateException ex) {
1255             Rlog.e(LOG_TAG, "hangupConnectionByIndex caught " + ex);
1256         }
1257     }
1258 
getConnectionByIndex(GsmCdmaCall call, int index)1259     public GsmCdmaConnection getConnectionByIndex(GsmCdmaCall call, int index)
1260             throws CallStateException {
1261         int count = call.mConnections.size();
1262         for (int i = 0; i < count; i++) {
1263             GsmCdmaConnection cn = (GsmCdmaConnection)call.mConnections.get(i);
1264             if (cn.getGsmCdmaIndex() == index) {
1265                 return cn;
1266             }
1267         }
1268 
1269         return null;
1270     }
1271 
1272     //CDMA
notifyCallWaitingInfo(CdmaCallWaitingNotification obj)1273     private void notifyCallWaitingInfo(CdmaCallWaitingNotification obj) {
1274         if (mCallWaitingRegistrants != null) {
1275             mCallWaitingRegistrants.notifyRegistrants(new AsyncResult(null, obj, null));
1276         }
1277     }
1278 
1279     //CDMA
handleCallWaitingInfo(CdmaCallWaitingNotification cw)1280     private void handleCallWaitingInfo(CdmaCallWaitingNotification cw) {
1281         // Create a new GsmCdmaConnection which attaches itself to ringingCall.
1282         new GsmCdmaConnection(mPhone.getContext(), cw, this, mRingingCall);
1283         updatePhoneState();
1284 
1285         // Finally notify application
1286         notifyCallWaitingInfo(cw);
1287     }
1288 
getFailedService(int what)1289     private Phone.SuppService getFailedService(int what) {
1290         switch (what) {
1291             case EVENT_SWITCH_RESULT:
1292                 return Phone.SuppService.SWITCH;
1293             case EVENT_CONFERENCE_RESULT:
1294                 return Phone.SuppService.CONFERENCE;
1295             case EVENT_SEPARATE_RESULT:
1296                 return Phone.SuppService.SEPARATE;
1297             case EVENT_ECT_RESULT:
1298                 return Phone.SuppService.TRANSFER;
1299         }
1300         return Phone.SuppService.UNKNOWN;
1301     }
1302 
1303     //****** Overridden from Handler
1304 
1305     @Override
handleMessage(Message msg)1306     public void handleMessage(Message msg) {
1307         AsyncResult ar;
1308 
1309         switch (msg.what) {
1310             case EVENT_POLL_CALLS_RESULT:
1311                 Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
1312 
1313                 if (msg == mLastRelevantPoll) {
1314                     if (DBG_POLL) log(
1315                             "handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
1316                     mNeedsPoll = false;
1317                     mLastRelevantPoll = null;
1318                     handlePollCalls((AsyncResult)msg.obj);
1319                 }
1320             break;
1321 
1322             case EVENT_OPERATION_COMPLETE:
1323                 operationComplete();
1324             break;
1325 
1326             case EVENT_CONFERENCE_RESULT:
1327                 if (isPhoneTypeGsm()) {
1328                     // The conference merge failed, so notify listeners.  Ultimately this bubbles up
1329                     // to Telecom, which will inform the InCall UI of the failure.
1330                     Connection connection = mForegroundCall.getLatestConnection();
1331                     if (connection != null) {
1332                         connection.onConferenceMergeFailed();
1333                     }
1334                 }
1335                 // fall through
1336             case EVENT_SEPARATE_RESULT:
1337             case EVENT_ECT_RESULT:
1338             case EVENT_SWITCH_RESULT:
1339                 if (isPhoneTypeGsm()) {
1340                     ar = (AsyncResult) msg.obj;
1341                     if (ar.exception != null) {
1342                         mPhone.notifySuppServiceFailed(getFailedService(msg.what));
1343                     }
1344                     operationComplete();
1345                 } else {
1346                     if (msg.what != EVENT_SWITCH_RESULT) {
1347                         // EVENT_SWITCH_RESULT in GSM call triggers operationComplete() which gets
1348                         // the current call list. But in CDMA there is no list so there is nothing
1349                         // to do. Other messages however are not expected in CDMA.
1350                         throw new RuntimeException("unexpected event " + msg.what + " not handled by " +
1351                                 "phone type " + mPhone.getPhoneType());
1352                     }
1353                 }
1354             break;
1355 
1356             case EVENT_GET_LAST_CALL_FAIL_CAUSE:
1357                 int causeCode;
1358                 String vendorCause = null;
1359                 ar = (AsyncResult)msg.obj;
1360 
1361                 operationComplete();
1362 
1363                 if (ar.exception != null) {
1364                     // An exception occurred...just treat the disconnect
1365                     // cause as "normal"
1366                     causeCode = CallFailCause.NORMAL_CLEARING;
1367                     Rlog.i(LOG_TAG,
1368                             "Exception during getLastCallFailCause, assuming normal disconnect");
1369                 } else {
1370                     LastCallFailCause failCause = (LastCallFailCause)ar.result;
1371                     causeCode = failCause.causeCode;
1372                     vendorCause = failCause.vendorCause;
1373                 }
1374                 // Log the causeCode if its not normal
1375                 if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL ||
1376                     causeCode == CallFailCause.TEMPORARY_FAILURE ||
1377                     causeCode == CallFailCause.SWITCHING_CONGESTION ||
1378                     causeCode == CallFailCause.CHANNEL_NOT_AVAIL ||
1379                     causeCode == CallFailCause.QOS_NOT_AVAIL ||
1380                     causeCode == CallFailCause.BEARER_NOT_AVAIL ||
1381                     causeCode == CallFailCause.ERROR_UNSPECIFIED) {
1382 
1383                     CellLocation loc = mPhone.getCellLocation();
1384                     int cid = -1;
1385                     if (loc != null) {
1386                         if (isPhoneTypeGsm()) {
1387                             cid = ((GsmCellLocation)loc).getCid();
1388                         } else {
1389                             cid = ((CdmaCellLocation)loc).getBaseStationId();
1390                         }
1391                     }
1392                     EventLog.writeEvent(EventLogTags.CALL_DROP, causeCode, cid,
1393                             TelephonyManager.getDefault().getNetworkType());
1394                 }
1395 
1396                 for (int i = 0, s = mDroppedDuringPoll.size(); i < s ; i++) {
1397                     GsmCdmaConnection conn = mDroppedDuringPoll.get(i);
1398 
1399                     conn.onRemoteDisconnect(causeCode, vendorCause);
1400                 }
1401 
1402                 updatePhoneState();
1403 
1404                 mPhone.notifyPreciseCallStateChanged();
1405                 mDroppedDuringPoll.clear();
1406             break;
1407 
1408             case EVENT_REPOLL_AFTER_DELAY:
1409             case EVENT_CALL_STATE_CHANGE:
1410                 pollCallsWhenSafe();
1411             break;
1412 
1413             case EVENT_RADIO_AVAILABLE:
1414                 handleRadioAvailable();
1415             break;
1416 
1417             case EVENT_RADIO_NOT_AVAILABLE:
1418                 handleRadioNotAvailable();
1419             break;
1420 
1421             case EVENT_EXIT_ECM_RESPONSE_CDMA:
1422                 if (!isPhoneTypeGsm()) {
1423                     // no matter the result, we still do the same here
1424                     if (mPendingCallInEcm) {
1425                         mCi.dial(mPendingMO.getAddress(), mPendingCallClirMode, obtainCompleteMessage());
1426                         mPendingCallInEcm = false;
1427                     }
1428                     mPhone.unsetOnEcbModeExitResponse(this);
1429                 } else {
1430                     throw new RuntimeException("unexpected event " + msg.what + " not handled by " +
1431                             "phone type " + mPhone.getPhoneType());
1432                 }
1433                 break;
1434 
1435             case EVENT_CALL_WAITING_INFO_CDMA:
1436                 if (!isPhoneTypeGsm()) {
1437                     ar = (AsyncResult)msg.obj;
1438                     if (ar.exception == null) {
1439                         handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result);
1440                         Rlog.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received");
1441                     }
1442                 } else {
1443                     throw new RuntimeException("unexpected event " + msg.what + " not handled by " +
1444                             "phone type " + mPhone.getPhoneType());
1445                 }
1446                 break;
1447 
1448             case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA:
1449                 if (!isPhoneTypeGsm()) {
1450                     ar = (AsyncResult)msg.obj;
1451                     if (ar.exception == null) {
1452                         // Assume 3 way call is connected
1453                         mPendingMO.onConnectedInOrOut();
1454                         mPendingMO = null;
1455                     }
1456                 } else {
1457                     throw new RuntimeException("unexpected event " + msg.what + " not handled by " +
1458                             "phone type " + mPhone.getPhoneType());
1459                 }
1460                 break;
1461 
1462             case EVENT_THREE_WAY_DIAL_BLANK_FLASH:
1463                 if (!isPhoneTypeGsm()) {
1464                     ar = (AsyncResult) msg.obj;
1465                     if (ar.exception == null) {
1466                         postDelayed(
1467                                 new Runnable() {
1468                                     public void run() {
1469                                         if (mPendingMO != null) {
1470                                             mCi.sendCDMAFeatureCode(mPendingMO.getAddress(),
1471                                                     obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA));
1472                                         }
1473                                     }
1474                                 }, m3WayCallFlashDelay);
1475                     } else {
1476                         mPendingMO = null;
1477                         Rlog.w(LOG_TAG, "exception happened on Blank Flash for 3-way call");
1478                     }
1479                 } else {
1480                     throw new RuntimeException("unexpected event " + msg.what + " not handled by " +
1481                             "phone type " + mPhone.getPhoneType());
1482                 }
1483                 break;
1484 
1485             default:{
1486                 throw new RuntimeException("unexpected event " + msg.what + " not handled by " +
1487                         "phone type " + mPhone.getPhoneType());
1488             }
1489         }
1490     }
1491 
1492     //CDMA
1493     /**
1494      * Check and enable data call after an emergency call is dropped if it's
1495      * not in ECM
1496      */
checkAndEnableDataCallAfterEmergencyCallDropped()1497     private void checkAndEnableDataCallAfterEmergencyCallDropped() {
1498         if (mIsInEmergencyCall) {
1499             mIsInEmergencyCall = false;
1500             String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
1501             if (Phone.DEBUG_PHONE) {
1502                 log("checkAndEnableDataCallAfterEmergencyCallDropped,inEcm=" + inEcm);
1503             }
1504             if (inEcm.compareTo("false") == 0) {
1505                 // Re-initiate data connection
1506                 mPhone.mDcTracker.setInternalDataEnabled(true);
1507                 mPhone.notifyEmergencyCallRegistrants(false);
1508             }
1509             mPhone.sendEmergencyCallStateChange(false);
1510         }
1511     }
1512 
1513     /**
1514      * Check the MT call to see if it's a new ring or
1515      * a unknown connection.
1516      */
checkMtFindNewRinging(DriverCall dc, int i)1517     private Connection checkMtFindNewRinging(DriverCall dc, int i) {
1518 
1519         Connection newRinging = null;
1520 
1521         // it's a ringing call
1522         if (mConnections[i].getCall() == mRingingCall) {
1523             newRinging = mConnections[i];
1524             if (Phone.DEBUG_PHONE) log("Notify new ring " + dc);
1525         } else {
1526             // Something strange happened: a call which is neither
1527             // a ringing call nor the one we created. It could be the
1528             // call collision result from RIL
1529             Rlog.e(LOG_TAG,"Phantom call appeared " + dc);
1530             // If it's a connected call, set the connect time so that
1531             // it's non-zero.  It may not be accurate, but at least
1532             // it won't appear as a Missed Call.
1533             if (dc.state != DriverCall.State.ALERTING
1534                     && dc.state != DriverCall.State.DIALING) {
1535                 mConnections[i].onConnectedInOrOut();
1536                 if (dc.state == DriverCall.State.HOLDING) {
1537                     // We've transitioned into HOLDING
1538                     mConnections[i].onStartedHolding();
1539                 }
1540             }
1541         }
1542         return newRinging;
1543     }
1544 
1545     //CDMA
1546     /**
1547      * Check if current call is in emergency call
1548      *
1549      * @return true if it is in emergency call
1550      *         false if it is not in emergency call
1551      */
isInEmergencyCall()1552     public boolean isInEmergencyCall() {
1553         return mIsInEmergencyCall;
1554     }
1555 
isPhoneTypeGsm()1556     private boolean isPhoneTypeGsm() {
1557         return mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM;
1558     }
1559 
getPhone()1560     public GsmCdmaPhone getPhone() {
1561         return mPhone;
1562     }
1563 
1564     @Override
log(String msg)1565     protected void log(String msg) {
1566         Rlog.d(LOG_TAG, "[GsmCdmaCallTracker] " + msg);
1567     }
1568 
1569     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)1570     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1571         pw.println("GsmCdmaCallTracker extends:");
1572         super.dump(fd, pw, args);
1573         pw.println("mConnections: length=" + mConnections.length);
1574         for(int i=0; i < mConnections.length; i++) {
1575             pw.printf("  mConnections[%d]=%s\n", i, mConnections[i]);
1576         }
1577         pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants);
1578         pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants);
1579         if (!isPhoneTypeGsm()) {
1580             pw.println(" mCallWaitingRegistrants=" + mCallWaitingRegistrants);
1581         }
1582         pw.println(" mDroppedDuringPoll: size=" + mDroppedDuringPoll.size());
1583         for(int i = 0; i < mDroppedDuringPoll.size(); i++) {
1584             pw.printf( "  mDroppedDuringPoll[%d]=%s\n", i, mDroppedDuringPoll.get(i));
1585         }
1586         pw.println(" mRingingCall=" + mRingingCall);
1587         pw.println(" mForegroundCall=" + mForegroundCall);
1588         pw.println(" mBackgroundCall=" + mBackgroundCall);
1589         pw.println(" mPendingMO=" + mPendingMO);
1590         pw.println(" mHangupPendingMO=" + mHangupPendingMO);
1591         pw.println(" mPhone=" + mPhone);
1592         pw.println(" mDesiredMute=" + mDesiredMute);
1593         pw.println(" mState=" + mState);
1594         if (!isPhoneTypeGsm()) {
1595             pw.println(" mPendingCallInEcm=" + mPendingCallInEcm);
1596             pw.println(" mIsInEmergencyCall=" + mIsInEmergencyCall);
1597             pw.println(" mPendingCallClirMode=" + mPendingCallClirMode);
1598             pw.println(" mIsEcmTimerCanceled=" + mIsEcmTimerCanceled);
1599         }
1600 
1601     }
1602 
1603     @Override
getState()1604     public PhoneConstants.State getState() {
1605         return mState;
1606     }
1607 
getMaxConnectionsPerCall()1608     public int getMaxConnectionsPerCall() {
1609         return mPhone.isPhoneTypeGsm() ?
1610                 MAX_CONNECTIONS_PER_CALL_GSM :
1611                 MAX_CONNECTIONS_PER_CALL_CDMA;
1612     }
1613 }
1614