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