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