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