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