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