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