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