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