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