• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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.cdma;
18 
19 import android.os.AsyncResult;
20 import android.os.Handler;
21 import android.os.Message;
22 import android.os.Registrant;
23 import android.os.RegistrantList;
24 import android.telephony.PhoneNumberUtils;
25 import android.telephony.ServiceState;
26 import android.util.Log;
27 import android.os.SystemProperties;
28 
29 import com.android.internal.telephony.CallStateException;
30 import com.android.internal.telephony.CallTracker;
31 import com.android.internal.telephony.CommandsInterface;
32 import com.android.internal.telephony.Connection;
33 import com.android.internal.telephony.DriverCall;
34 import com.android.internal.telephony.Phone;
35 import com.android.internal.telephony.TelephonyProperties;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 
41 /**
42  * {@hide}
43  */
44 public final class CdmaCallTracker extends CallTracker {
45     static final String LOG_TAG = "CDMA";
46 
47     private static final boolean REPEAT_POLLING = false;
48 
49     private static final boolean DBG_POLL = false;
50 
51     //***** Constants
52 
53     static final int MAX_CONNECTIONS = 1;   // only 1 connection allowed in CDMA
54     static final int MAX_CONNECTIONS_PER_CALL = 1; // only 1 connection allowed per call
55 
56     //***** Instance Variables
57 
58     CdmaConnection connections[] = new CdmaConnection[MAX_CONNECTIONS];
59     RegistrantList voiceCallEndedRegistrants = new RegistrantList();
60     RegistrantList voiceCallStartedRegistrants = new RegistrantList();
61     RegistrantList callWaitingRegistrants =  new RegistrantList();
62 
63 
64     // connections dropped durin last poll
65     ArrayList<CdmaConnection> droppedDuringPoll
66         = new ArrayList<CdmaConnection>(MAX_CONNECTIONS);
67 
68     CdmaCall ringingCall = new CdmaCall(this);
69     // A call that is ringing or (call) waiting
70     CdmaCall foregroundCall = new CdmaCall(this);
71     CdmaCall backgroundCall = new CdmaCall(this);
72 
73     CdmaConnection pendingMO;
74     boolean hangupPendingMO;
75     boolean pendingCallInEcm=false;
76     boolean mIsInEmergencyCall = false;
77     CDMAPhone phone;
78 
79     boolean desiredMute = false;    // false = mute off
80 
81     int pendingCallClirMode;
82     Phone.State state = Phone.State.IDLE;
83 
84     private boolean mIsEcmTimerCanceled = false;
85 
86 //    boolean needsPoll;
87 
88 
89 
90     //***** Events
91 
92     //***** Constructors
CdmaCallTracker(CDMAPhone phone)93     CdmaCallTracker(CDMAPhone phone) {
94         this.phone = phone;
95         cm = phone.mCM;
96         cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
97         cm.registerForOn(this, EVENT_RADIO_AVAILABLE, null);
98         cm.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);
99         cm.registerForCallWaitingInfo(this, EVENT_CALL_WAITING_INFO_CDMA, null);
100         foregroundCall.setGeneric(false);
101     }
102 
dispose()103     public void dispose() {
104         cm.unregisterForCallStateChanged(this);
105         cm.unregisterForOn(this);
106         cm.unregisterForNotAvailable(this);
107         cm.unregisterForCallWaitingInfo(this);
108         for(CdmaConnection c : connections) {
109             try {
110                 if(c != null) hangup(c);
111             } catch (CallStateException ex) {
112                 Log.e(LOG_TAG, "unexpected error on hangup during dispose");
113             }
114         }
115 
116         try {
117             if(pendingMO != null) hangup(pendingMO);
118         } catch (CallStateException ex) {
119             Log.e(LOG_TAG, "unexpected error on hangup during dispose");
120         }
121 
122         clearDisconnected();
123 
124     }
125 
126     @Override
finalize()127     protected void finalize() {
128         Log.d(LOG_TAG, "CdmaCallTracker finalized");
129     }
130 
131     //***** Instance Methods
132 
133     //***** Public Methods
registerForVoiceCallStarted(Handler h, int what, Object obj)134     public void registerForVoiceCallStarted(Handler h, int what, Object obj) {
135         Registrant r = new Registrant(h, what, obj);
136         voiceCallStartedRegistrants.add(r);
137     }
unregisterForVoiceCallStarted(Handler h)138     public void unregisterForVoiceCallStarted(Handler h) {
139         voiceCallStartedRegistrants.remove(h);
140     }
141 
registerForVoiceCallEnded(Handler h, int what, Object obj)142     public void registerForVoiceCallEnded(Handler h, int what, Object obj) {
143         Registrant r = new Registrant(h, what, obj);
144         voiceCallEndedRegistrants.add(r);
145     }
146 
unregisterForVoiceCallEnded(Handler h)147     public void unregisterForVoiceCallEnded(Handler h) {
148         voiceCallEndedRegistrants.remove(h);
149     }
150 
registerForCallWaiting(Handler h, int what, Object obj)151     public void registerForCallWaiting(Handler h, int what, Object obj) {
152         Registrant r = new Registrant (h, what, obj);
153         callWaitingRegistrants.add(r);
154     }
155 
unregisterForCallWaiting(Handler h)156     public void unregisterForCallWaiting(Handler h) {
157         callWaitingRegistrants.remove(h);
158     }
159 
160     private void
fakeHoldForegroundBeforeDial()161     fakeHoldForegroundBeforeDial() {
162         List<Connection> connCopy;
163 
164         // We need to make a copy here, since fakeHoldBeforeDial()
165         // modifies the lists, and we don't want to reverse the order
166         connCopy = (List<Connection>) foregroundCall.connections.clone();
167 
168         for (int i = 0, s = connCopy.size() ; i < s ; i++) {
169             CdmaConnection conn = (CdmaConnection)connCopy.get(i);
170 
171             conn.fakeHoldBeforeDial();
172         }
173     }
174 
175     /**
176      * clirMode is one of the CLIR_ constants
177      */
178     Connection
dial(String dialString, int clirMode)179     dial (String dialString, int clirMode) throws CallStateException {
180         // note that this triggers call state changed notif
181         clearDisconnected();
182 
183         if (!canDial()) {
184             throw new CallStateException("cannot dial in current state");
185         }
186 
187         String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
188         boolean isPhoneInEcmMode = inEcm.equals("true");
189         boolean isEmergencyCall = PhoneNumberUtils.isEmergencyNumber(dialString);
190 
191         // Cancel Ecm timer if a second emergency call is originating in Ecm mode
192         if (isPhoneInEcmMode && isEmergencyCall) {
193             handleEcmTimer(phone.CANCEL_ECM_TIMER);
194         }
195 
196         // We are initiating a call therefore even if we previously
197         // didn't know the state (i.e. Generic was true) we now know
198         // and therefore can set Generic to false.
199         foregroundCall.setGeneric(false);
200 
201         // The new call must be assigned to the foreground call.
202         // That call must be idle, so place anything that's
203         // there on hold
204         if (foregroundCall.getState() == CdmaCall.State.ACTIVE) {
205             return dialThreeWay(dialString);
206         }
207 
208         pendingMO = new CdmaConnection(phone.getContext(), dialString, this, foregroundCall);
209         hangupPendingMO = false;
210 
211         if (pendingMO.address == null || pendingMO.address.length() == 0
212             || pendingMO.address.indexOf(PhoneNumberUtils.WILD) >= 0) {
213             // Phone number is invalid
214             pendingMO.cause = Connection.DisconnectCause.INVALID_NUMBER;
215 
216             // handlePollCalls() will notice this call not present
217             // and will mark it as dropped.
218             pollCallsWhenSafe();
219         } else {
220             // Always unmute when initiating a new call
221             setMute(false);
222 
223             // Check data call
224             disableDataCallInEmergencyCall(dialString);
225 
226             // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
227             if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
228                 cm.dial(pendingMO.address, clirMode, obtainCompleteMessage());
229             } else {
230                 phone.exitEmergencyCallbackMode();
231                 phone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null);
232                 pendingCallClirMode=clirMode;
233                 pendingCallInEcm=true;
234             }
235         }
236 
237         updatePhoneState();
238         phone.notifyPreciseCallStateChanged();
239 
240         return pendingMO;
241     }
242 
243 
244     Connection
dial(String dialString)245     dial (String dialString) throws CallStateException {
246         return dial(dialString, CommandsInterface.CLIR_DEFAULT);
247     }
248 
249     private Connection
dialThreeWay(String dialString)250     dialThreeWay (String dialString) {
251         if (!foregroundCall.isIdle()) {
252             // Check data call
253             disableDataCallInEmergencyCall(dialString);
254 
255             // Attach the new connection to foregroundCall
256             pendingMO = new CdmaConnection(phone.getContext(),
257                                 dialString, this, foregroundCall);
258             cm.sendCDMAFeatureCode(pendingMO.address,
259                 obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA));
260             return pendingMO;
261         }
262         return null;
263     }
264 
265     void
acceptCall()266     acceptCall() throws CallStateException {
267         if (ringingCall.getState() == CdmaCall.State.INCOMING) {
268             Log.i("phone", "acceptCall: incoming...");
269             // Always unmute when answering a new call
270             setMute(false);
271             cm.acceptCall(obtainCompleteMessage());
272         } else if (ringingCall.getState() == CdmaCall.State.WAITING) {
273             CdmaConnection cwConn = (CdmaConnection)(ringingCall.getLatestConnection());
274 
275             // Since there is no network response for supplimentary
276             // service for CDMA, we assume call waiting is answered.
277             // ringing Call state change to idle is in CdmaCall.detach
278             // triggered by updateParent.
279             cwConn.updateParent(ringingCall, foregroundCall);
280             cwConn.onConnectedInOrOut();
281             updatePhoneState();
282             switchWaitingOrHoldingAndActive();
283         } else {
284             throw new CallStateException("phone not ringing");
285         }
286     }
287 
288     void
rejectCall()289     rejectCall () throws CallStateException {
290         // AT+CHLD=0 means "release held or UDUB"
291         // so if the phone isn't ringing, this could hang up held
292         if (ringingCall.getState().isRinging()) {
293             cm.rejectCall(obtainCompleteMessage());
294         } else {
295             throw new CallStateException("phone not ringing");
296         }
297     }
298 
299     void
switchWaitingOrHoldingAndActive()300     switchWaitingOrHoldingAndActive() throws CallStateException {
301         // Should we bother with this check?
302         if (ringingCall.getState() == CdmaCall.State.INCOMING) {
303             throw new CallStateException("cannot be in the incoming state");
304         } else if (foregroundCall.getConnections().size() > 1) {
305             flashAndSetGenericTrue();
306         } else {
307             // Send a flash command to CDMA network for putting the other party on hold.
308             // For CDMA networks which do not support this the user would just hear a beep
309             // from the network. For CDMA networks which do support it will put the other
310             // party on hold.
311             cm.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT));
312         }
313     }
314 
315     void
conference()316     conference() throws CallStateException {
317         // Should we be checking state?
318         flashAndSetGenericTrue();
319     }
320 
321     void
explicitCallTransfer()322     explicitCallTransfer() throws CallStateException {
323         cm.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT));
324     }
325 
326     void
clearDisconnected()327     clearDisconnected() {
328         internalClearDisconnected();
329 
330         updatePhoneState();
331         phone.notifyPreciseCallStateChanged();
332     }
333 
334     boolean
canConference()335     canConference() {
336         return foregroundCall.getState() == CdmaCall.State.ACTIVE
337                 && backgroundCall.getState() == CdmaCall.State.HOLDING
338                 && !backgroundCall.isFull()
339                 && !foregroundCall.isFull();
340     }
341 
342     boolean
canDial()343     canDial() {
344         boolean ret;
345         int serviceState = phone.getServiceState().getState();
346         String disableCall = SystemProperties.get(
347                 TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
348 
349         ret = (serviceState != ServiceState.STATE_POWER_OFF)
350                 && pendingMO == null
351                 && !ringingCall.isRinging()
352                 && !disableCall.equals("true")
353                 && (!foregroundCall.getState().isAlive()
354                     || (foregroundCall.getState() == CdmaCall.State.ACTIVE)
355                     || !backgroundCall.getState().isAlive());
356 
357         return ret;
358     }
359 
360     boolean
canTransfer()361     canTransfer() {
362         Log.e(LOG_TAG, "canTransfer: not possible in CDMA");
363         return false;
364     }
365 
366     //***** Private Instance Methods
367 
368     private void
internalClearDisconnected()369     internalClearDisconnected() {
370         ringingCall.clearDisconnected();
371         foregroundCall.clearDisconnected();
372         backgroundCall.clearDisconnected();
373     }
374 
375     /**
376      * Obtain a message to use for signalling "invoke getCurrentCalls() when
377      * this operation and all other pending operations are complete
378      */
379     private Message
obtainCompleteMessage()380     obtainCompleteMessage() {
381         return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
382     }
383 
384     /**
385      * Obtain a message to use for signalling "invoke getCurrentCalls() when
386      * this operation and all other pending operations are complete
387      */
388     private Message
obtainCompleteMessage(int what)389     obtainCompleteMessage(int what) {
390         pendingOperations++;
391         lastRelevantPoll = null;
392         needsPoll = true;
393 
394         if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" +
395                 pendingOperations + ", needsPoll=" + needsPoll);
396 
397         return obtainMessage(what);
398     }
399 
400     private void
operationComplete()401     operationComplete() {
402         pendingOperations--;
403 
404         if (DBG_POLL) log("operationComplete: pendingOperations=" +
405                 pendingOperations + ", needsPoll=" + needsPoll);
406 
407         if (pendingOperations == 0 && needsPoll) {
408             lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
409             cm.getCurrentCalls(lastRelevantPoll);
410         } else if (pendingOperations < 0) {
411             // this should never happen
412             Log.e(LOG_TAG,"CdmaCallTracker.pendingOperations < 0");
413             pendingOperations = 0;
414         }
415     }
416 
417 
418 
419     private void
updatePhoneState()420     updatePhoneState() {
421         Phone.State oldState = state;
422 
423         if (ringingCall.isRinging()) {
424             state = Phone.State.RINGING;
425         } else if (pendingMO != null ||
426                 !(foregroundCall.isIdle() && backgroundCall.isIdle())) {
427             state = Phone.State.OFFHOOK;
428         } else {
429             state = Phone.State.IDLE;
430         }
431 
432         if (state == Phone.State.IDLE && oldState != state) {
433             voiceCallEndedRegistrants.notifyRegistrants(
434                 new AsyncResult(null, null, null));
435         } else if (oldState == Phone.State.IDLE && oldState != state) {
436             voiceCallStartedRegistrants.notifyRegistrants (
437                     new AsyncResult(null, null, null));
438         }
439         if (Phone.DEBUG_PHONE) {
440             log("update phone state, old=" + oldState + " new="+ state);
441         }
442         if (state != oldState) {
443             phone.notifyPhoneStateChanged();
444         }
445     }
446 
447     // ***** Overwritten from CallTracker
448 
449     protected void
handlePollCalls(AsyncResult ar)450     handlePollCalls(AsyncResult ar) {
451         List polledCalls;
452 
453         if (ar.exception == null) {
454             polledCalls = (List)ar.result;
455         } else if (isCommandExceptionRadioNotAvailable(ar.exception)) {
456             // just a dummy empty ArrayList to cause the loop
457             // to hang up all the calls
458             polledCalls = new ArrayList();
459         } else {
460             // Radio probably wasn't ready--try again in a bit
461             // But don't keep polling if the channel is closed
462             pollCallsAfterDelay();
463             return;
464         }
465 
466         Connection newRinging = null; //or waiting
467         boolean hasNonHangupStateChanged = false;   // Any change besides
468                                                     // a dropped connection
469         boolean needsPollDelay = false;
470         boolean unknownConnectionAppeared = false;
471 
472         for (int i = 0, curDC = 0, dcSize = polledCalls.size()
473                 ; i < connections.length; i++) {
474             CdmaConnection conn = connections[i];
475             DriverCall dc = null;
476 
477             // polledCall list is sparse
478             if (curDC < dcSize) {
479                 dc = (DriverCall) polledCalls.get(curDC);
480 
481                 if (dc.index == i+1) {
482                     curDC++;
483                 } else {
484                     dc = null;
485                 }
486             }
487 
488             if (DBG_POLL) log("poll: conn[i=" + i + "]=" +
489                     conn+", dc=" + dc);
490 
491             if (conn == null && dc != null) {
492                 // Connection appeared in CLCC response that we don't know about
493                 if (pendingMO != null && pendingMO.compareTo(dc)) {
494 
495                     if (DBG_POLL) log("poll: pendingMO=" + pendingMO);
496 
497                     // It's our pending mobile originating call
498                     connections[i] = pendingMO;
499                     pendingMO.index = i;
500                     pendingMO.update(dc);
501                     pendingMO = null;
502 
503                     // Someone has already asked to hangup this call
504                     if (hangupPendingMO) {
505                         hangupPendingMO = false;
506                         // Re-start Ecm timer when an uncompleted emergency call ends
507                         if (mIsEcmTimerCanceled) {
508                             handleEcmTimer(phone.RESTART_ECM_TIMER);
509                         }
510 
511                         try {
512                             if (Phone.DEBUG_PHONE) log(
513                                     "poll: hangupPendingMO, hangup conn " + i);
514                             hangup(connections[i]);
515                         } catch (CallStateException ex) {
516                             Log.e(LOG_TAG, "unexpected error on hangup");
517                         }
518 
519                         // Do not continue processing this poll
520                         // Wait for hangup and repoll
521                         return;
522                     }
523                 } else {
524                     if (Phone.DEBUG_PHONE) {
525                         log("pendingMo=" + pendingMO + ", dc=" + dc);
526                     }
527                     // find if the MT call is a new ring or unknown connection
528                     newRinging = checkMtFindNewRinging(dc,i);
529                     if (newRinging == null) {
530                         unknownConnectionAppeared = true;
531                     }
532                     checkAndEnableDataCallAfterEmergencyCallDropped();
533                 }
534                 hasNonHangupStateChanged = true;
535             } else if (conn != null && dc == null) {
536                 // This case means the RIL has no more active call anymore and
537                 // we need to clean up the foregroundCall and ringingCall.
538                 // Loop through foreground call connections as
539                 // it contains the known logical connections.
540                 int count = foregroundCall.connections.size();
541                 for (int n = 0; n < count; n++) {
542                     if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll");
543                     CdmaConnection cn = (CdmaConnection)foregroundCall.connections.get(n);
544                     droppedDuringPoll.add(cn);
545                 }
546                 count = ringingCall.connections.size();
547                 // Loop through ringing call connections as
548                 // it may contain the known logical connections.
549                 for (int n = 0; n < count; n++) {
550                     if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll");
551                     CdmaConnection cn = (CdmaConnection)ringingCall.connections.get(n);
552                     droppedDuringPoll.add(cn);
553                 }
554                 foregroundCall.setGeneric(false);
555                 ringingCall.setGeneric(false);
556 
557                 // Re-start Ecm timer when the connected emergency call ends
558                 if (mIsEcmTimerCanceled) {
559                     handleEcmTimer(phone.RESTART_ECM_TIMER);
560                 }
561                 // If emergency call is not going through while dialing
562                 checkAndEnableDataCallAfterEmergencyCallDropped();
563 
564                 // Dropped connections are removed from the CallTracker
565                 // list but kept in the Call list
566                 connections[i] = null;
567             } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
568                 // Call collision case
569                 if (conn.isIncoming != dc.isMT) {
570                     if (dc.isMT == true){
571                         // Mt call takes precedence than Mo,drops Mo
572                         droppedDuringPoll.add(conn);
573                         // find if the MT call is a new ring or unknown connection
574                         newRinging = checkMtFindNewRinging(dc,i);
575                         if (newRinging == null) {
576                             unknownConnectionAppeared = true;
577                         }
578                         checkAndEnableDataCallAfterEmergencyCallDropped();
579                     } else {
580                         // Call info stored in conn is not consistent with the call info from dc.
581                         // We should follow the rule of MT calls taking precedence over MO calls
582                         // when there is conflict, so here we drop the call info from dc and
583                         // continue to use the call info from conn, and only take a log.
584                         Log.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc);
585                     }
586                 } else {
587                     boolean changed;
588                     changed = conn.update(dc);
589                     hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
590                 }
591             }
592 
593             if (REPEAT_POLLING) {
594                 if (dc != null) {
595                     // FIXME with RIL, we should not need this anymore
596                     if ((dc.state == DriverCall.State.DIALING
597                             /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/)
598                         || (dc.state == DriverCall.State.ALERTING
599                             /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/)
600                         || (dc.state == DriverCall.State.INCOMING
601                             /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/)
602                         || (dc.state == DriverCall.State.WAITING
603                             /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)
604                     ) {
605                         // Sometimes there's no unsolicited notification
606                         // for state transitions
607                         needsPollDelay = true;
608                     }
609                 }
610             }
611         }
612 
613         // This is the first poll after an ATD.
614         // We expect the pending call to appear in the list
615         // If it does not, we land here
616         if (pendingMO != null) {
617             Log.d(LOG_TAG,"Pending MO dropped before poll fg state:"
618                             + foregroundCall.getState());
619 
620             droppedDuringPoll.add(pendingMO);
621             pendingMO = null;
622             hangupPendingMO = false;
623             if( pendingCallInEcm) {
624                 pendingCallInEcm = false;
625             }
626         }
627 
628         if (newRinging != null) {
629             phone.notifyNewRingingConnection(newRinging);
630         }
631 
632         // clear the "local hangup" and "missed/rejected call"
633         // cases from the "dropped during poll" list
634         // These cases need no "last call fail" reason
635         for (int i = droppedDuringPoll.size() - 1; i >= 0 ; i--) {
636             CdmaConnection conn = droppedDuringPoll.get(i);
637 
638             if (conn.isIncoming() && conn.getConnectTime() == 0) {
639                 // Missed or rejected call
640                 Connection.DisconnectCause cause;
641                 if (conn.cause == Connection.DisconnectCause.LOCAL) {
642                     cause = Connection.DisconnectCause.INCOMING_REJECTED;
643                 } else {
644                     cause = Connection.DisconnectCause.INCOMING_MISSED;
645                 }
646 
647                 if (Phone.DEBUG_PHONE) {
648                     log("missed/rejected call, conn.cause=" + conn.cause);
649                     log("setting cause to " + cause);
650                 }
651                 droppedDuringPoll.remove(i);
652                 conn.onDisconnect(cause);
653             } else if (conn.cause == Connection.DisconnectCause.LOCAL) {
654                 // Local hangup
655                 droppedDuringPoll.remove(i);
656                 conn.onDisconnect(Connection.DisconnectCause.LOCAL);
657             } else if (conn.cause == Connection.DisconnectCause.INVALID_NUMBER) {
658                 droppedDuringPoll.remove(i);
659                 conn.onDisconnect(Connection.DisconnectCause.INVALID_NUMBER);
660             }
661         }
662 
663         // Any non-local disconnects: determine cause
664         if (droppedDuringPoll.size() > 0) {
665             cm.getLastCallFailCause(
666                 obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
667         }
668 
669         if (needsPollDelay) {
670             pollCallsAfterDelay();
671         }
672 
673         // Cases when we can no longer keep disconnected Connection's
674         // with their previous calls
675         // 1) the phone has started to ring
676         // 2) A Call/Connection object has changed state...
677         //    we may have switched or held or answered (but not hung up)
678         if (newRinging != null || hasNonHangupStateChanged) {
679             internalClearDisconnected();
680         }
681 
682         updatePhoneState();
683 
684         if (unknownConnectionAppeared) {
685             phone.notifyUnknownConnection();
686         }
687 
688         if (hasNonHangupStateChanged || newRinging != null) {
689             phone.notifyPreciseCallStateChanged();
690         }
691 
692         //dumpState();
693     }
694 
695     //***** Called from CdmaConnection
696     /*package*/ void
hangup(CdmaConnection conn)697     hangup (CdmaConnection conn) throws CallStateException {
698         if (conn.owner != this) {
699             throw new CallStateException ("CdmaConnection " + conn
700                                     + "does not belong to CdmaCallTracker " + this);
701         }
702 
703         if (conn == pendingMO) {
704             // We're hanging up an outgoing call that doesn't have it's
705             // GSM index assigned yet
706 
707             if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true");
708             hangupPendingMO = true;
709         } else if ((conn.getCall() == ringingCall)
710                 && (ringingCall.getState() == CdmaCall.State.WAITING)) {
711             // Handle call waiting hang up case.
712             //
713             // The ringingCall state will change to IDLE in CdmaCall.detach
714             // if the ringing call connection size is 0. We don't specifically
715             // set the ringing call state to IDLE here to avoid a race condition
716             // where a new call waiting could get a hang up from an old call
717             // waiting ringingCall.
718             //
719             // PhoneApp does the call log itself since only PhoneApp knows
720             // the hangup reason is user ignoring or timing out. So conn.onDisconnect()
721             // is not called here. Instead, conn.onLocalDisconnect() is called.
722             conn.onLocalDisconnect();
723             updatePhoneState();
724             phone.notifyPreciseCallStateChanged();
725             return;
726         } else {
727             try {
728                 cm.hangupConnection (conn.getCDMAIndex(), obtainCompleteMessage());
729             } catch (CallStateException ex) {
730                 // Ignore "connection not found"
731                 // Call may have hung up already
732                 Log.w(LOG_TAG,"CdmaCallTracker WARN: hangup() on absent connection "
733                                 + conn);
734             }
735         }
736 
737         conn.onHangupLocal();
738     }
739 
740     /*package*/ void
separate(CdmaConnection conn)741     separate (CdmaConnection conn) throws CallStateException {
742         if (conn.owner != this) {
743             throw new CallStateException ("CdmaConnection " + conn
744                                     + "does not belong to CdmaCallTracker " + this);
745         }
746         try {
747             cm.separateConnection (conn.getCDMAIndex(),
748                 obtainCompleteMessage(EVENT_SEPARATE_RESULT));
749         } catch (CallStateException ex) {
750             // Ignore "connection not found"
751             // Call may have hung up already
752             Log.w(LOG_TAG,"CdmaCallTracker WARN: separate() on absent connection "
753                           + conn);
754         }
755     }
756 
757     //***** Called from CDMAPhone
758 
759     /*package*/ void
setMute(boolean mute)760     setMute(boolean mute) {
761         desiredMute = mute;
762         cm.setMute(desiredMute, null);
763     }
764 
765     /*package*/ boolean
getMute()766     getMute() {
767         return desiredMute;
768     }
769 
770 
771     //***** Called from CdmaCall
772 
773     /* package */ void
hangup(CdmaCall call)774     hangup (CdmaCall call) throws CallStateException {
775         if (call.getConnections().size() == 0) {
776             throw new CallStateException("no connections in call");
777         }
778 
779         if (call == ringingCall) {
780             if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background");
781             cm.hangupWaitingOrBackground(obtainCompleteMessage());
782         } else if (call == foregroundCall) {
783             if (call.isDialingOrAlerting()) {
784                 if (Phone.DEBUG_PHONE) {
785                     log("(foregnd) hangup dialing or alerting...");
786                 }
787                 hangup((CdmaConnection)(call.getConnections().get(0)));
788             } else {
789                 hangupForegroundResumeBackground();
790             }
791         } else if (call == backgroundCall) {
792             if (ringingCall.isRinging()) {
793                 if (Phone.DEBUG_PHONE) {
794                     log("hangup all conns in background call");
795                 }
796                 hangupAllConnections(call);
797             } else {
798                 hangupWaitingOrBackground();
799             }
800         } else {
801             throw new RuntimeException ("CdmaCall " + call +
802                     "does not belong to CdmaCallTracker " + this);
803         }
804 
805         call.onHangupLocal();
806         phone.notifyPreciseCallStateChanged();
807     }
808 
809     /* package */
hangupWaitingOrBackground()810     void hangupWaitingOrBackground() {
811         if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground");
812         cm.hangupWaitingOrBackground(obtainCompleteMessage());
813     }
814 
815     /* package */
hangupForegroundResumeBackground()816     void hangupForegroundResumeBackground() {
817         if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground");
818         cm.hangupForegroundResumeBackground(obtainCompleteMessage());
819     }
820 
hangupConnectionByIndex(CdmaCall call, int index)821     void hangupConnectionByIndex(CdmaCall call, int index)
822             throws CallStateException {
823         int count = call.connections.size();
824         for (int i = 0; i < count; i++) {
825             CdmaConnection cn = (CdmaConnection)call.connections.get(i);
826             if (cn.getCDMAIndex() == index) {
827                 cm.hangupConnection(index, obtainCompleteMessage());
828                 return;
829             }
830         }
831 
832         throw new CallStateException("no gsm index found");
833     }
834 
hangupAllConnections(CdmaCall call)835     void hangupAllConnections(CdmaCall call) throws CallStateException{
836         try {
837             int count = call.connections.size();
838             for (int i = 0; i < count; i++) {
839                 CdmaConnection cn = (CdmaConnection)call.connections.get(i);
840                 cm.hangupConnection(cn.getCDMAIndex(), obtainCompleteMessage());
841             }
842         } catch (CallStateException ex) {
843             Log.e(LOG_TAG, "hangupConnectionByIndex caught " + ex);
844         }
845     }
846 
847     /* package */
getConnectionByIndex(CdmaCall call, int index)848     CdmaConnection getConnectionByIndex(CdmaCall call, int index)
849             throws CallStateException {
850         int count = call.connections.size();
851         for (int i = 0; i < count; i++) {
852             CdmaConnection cn = (CdmaConnection)call.connections.get(i);
853             if (cn.getCDMAIndex() == index) {
854                 return cn;
855             }
856         }
857 
858         return null;
859     }
860 
flashAndSetGenericTrue()861     private void flashAndSetGenericTrue() throws CallStateException {
862         cm.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT));
863 
864         // Set generic to true because in CDMA it is not known what
865         // the status of the call is after a call waiting is answered,
866         // 3 way call merged or a switch between calls.
867         foregroundCall.setGeneric(true);
868         phone.notifyPreciseCallStateChanged();
869     }
870 
getFailedService(int what)871     private Phone.SuppService getFailedService(int what) {
872         switch (what) {
873             case EVENT_SWITCH_RESULT:
874                 return Phone.SuppService.SWITCH;
875             case EVENT_CONFERENCE_RESULT:
876                 return Phone.SuppService.CONFERENCE;
877             case EVENT_SEPARATE_RESULT:
878                 return Phone.SuppService.SEPARATE;
879             case EVENT_ECT_RESULT:
880                 return Phone.SuppService.TRANSFER;
881         }
882         return Phone.SuppService.UNKNOWN;
883     }
884 
handleRadioNotAvailable()885     private void handleRadioNotAvailable() {
886         // handlePollCalls will clear out its
887         // call list when it gets the CommandException
888         // error result from this
889         pollCallsWhenSafe();
890     }
891 
notifyCallWaitingInfo(CdmaCallWaitingNotification obj)892     private void notifyCallWaitingInfo(CdmaCallWaitingNotification obj) {
893         if (callWaitingRegistrants != null) {
894             callWaitingRegistrants.notifyRegistrants(new AsyncResult(null, obj, null));
895         }
896     }
897 
handleCallWaitingInfo(CdmaCallWaitingNotification cw)898     private void handleCallWaitingInfo (CdmaCallWaitingNotification cw) {
899         // Check how many connections in foregroundCall.
900         // If the connection in foregroundCall is more
901         // than one, then the connection information is
902         // not reliable anymore since it means either
903         // call waiting is connected or 3 way call is
904         // dialed before, so set generic.
905         if (foregroundCall.connections.size() > 1 ) {
906             foregroundCall.setGeneric(true);
907         }
908 
909         // Create a new CdmaConnection which attaches itself to ringingCall.
910         ringingCall.setGeneric(false);
911         new CdmaConnection(phone.getContext(), cw, this, ringingCall);
912         updatePhoneState();
913 
914         // Finally notify application
915         notifyCallWaitingInfo(cw);
916     }
917     //****** Overridden from Handler
918 
919     public void
handleMessage(Message msg)920     handleMessage (Message msg) {
921         AsyncResult ar;
922 
923         switch (msg.what) {
924             case EVENT_POLL_CALLS_RESULT:{
925                 Log.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
926                 ar = (AsyncResult)msg.obj;
927 
928                 if(msg == lastRelevantPoll) {
929                     if(DBG_POLL) log(
930                             "handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
931                     needsPoll = false;
932                     lastRelevantPoll = null;
933                     handlePollCalls((AsyncResult)msg.obj);
934                 }
935             }
936             break;
937 
938             case EVENT_OPERATION_COMPLETE:
939                 operationComplete();
940             break;
941 
942             case EVENT_SWITCH_RESULT:
943                  // In GSM call operationComplete() here which gets the
944                  // current call list. But in CDMA there is no list so
945                  // there is nothing to do.
946             break;
947 
948             case EVENT_GET_LAST_CALL_FAIL_CAUSE:
949                 int causeCode;
950                 ar = (AsyncResult)msg.obj;
951 
952                 operationComplete();
953 
954                 if (ar.exception != null) {
955                     // An exception occurred...just treat the disconnect
956                     // cause as "normal"
957                     causeCode = CallFailCause.NORMAL_CLEARING;
958                     Log.i(LOG_TAG,
959                             "Exception during getLastCallFailCause, assuming normal disconnect");
960                 } else {
961                     causeCode = ((int[])ar.result)[0];
962                 }
963 
964                 for (int i = 0, s =  droppedDuringPoll.size()
965                         ; i < s ; i++
966                 ) {
967                     CdmaConnection conn = droppedDuringPoll.get(i);
968 
969                     conn.onRemoteDisconnect(causeCode);
970                 }
971 
972                 updatePhoneState();
973 
974                 phone.notifyPreciseCallStateChanged();
975                 droppedDuringPoll.clear();
976             break;
977 
978             case EVENT_REPOLL_AFTER_DELAY:
979             case EVENT_CALL_STATE_CHANGE:
980                 pollCallsWhenSafe();
981             break;
982 
983             case EVENT_RADIO_AVAILABLE:
984                 handleRadioAvailable();
985             break;
986 
987             case EVENT_RADIO_NOT_AVAILABLE:
988                 handleRadioNotAvailable();
989             break;
990 
991             case EVENT_EXIT_ECM_RESPONSE_CDMA:
992                //no matter the result, we still do the same here
993                if (pendingCallInEcm) {
994                    cm.dial(pendingMO.address, pendingCallClirMode, obtainCompleteMessage());
995                    pendingCallInEcm = false;
996                }
997                phone.unsetOnEcbModeExitResponse(this);
998             break;
999 
1000             case EVENT_CALL_WAITING_INFO_CDMA:
1001                ar = (AsyncResult)msg.obj;
1002                if (ar.exception == null) {
1003                    handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result);
1004                    Log.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received");
1005                }
1006             break;
1007 
1008             case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA:
1009                 ar = (AsyncResult)msg.obj;
1010                 if (ar.exception == null) {
1011                     // Assume 3 way call is connected
1012                     pendingMO.onConnectedInOrOut();
1013                     pendingMO = null;
1014                 }
1015             break;
1016 
1017             default:{
1018                throw new RuntimeException("unexpected event not handled");
1019             }
1020         }
1021     }
1022 
1023     /**
1024      * Handle Ecm timer to be canceled or re-started
1025      */
handleEcmTimer(int action)1026     private void handleEcmTimer(int action) {
1027         phone.handleTimerInEmergencyCallbackMode(action);
1028         switch(action) {
1029         case CDMAPhone.CANCEL_ECM_TIMER: mIsEcmTimerCanceled = true; break;
1030         case CDMAPhone.RESTART_ECM_TIMER: mIsEcmTimerCanceled = false; break;
1031         default:
1032             Log.e(LOG_TAG, "handleEcmTimer, unsupported action " + action);
1033         }
1034     }
1035 
1036     /**
1037      * Disable data call when emergency call is connected
1038      */
disableDataCallInEmergencyCall(String dialString)1039     private void disableDataCallInEmergencyCall(String dialString) {
1040         if (PhoneNumberUtils.isEmergencyNumber(dialString)) {
1041             if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall");
1042             mIsInEmergencyCall = true;
1043             phone.disableDataConnectivity();
1044         }
1045     }
1046 
1047     /**
1048      * Check and enable data call after an emergency call is dropped if it's
1049      * not in ECM
1050      */
checkAndEnableDataCallAfterEmergencyCallDropped()1051     private void checkAndEnableDataCallAfterEmergencyCallDropped() {
1052         if (mIsInEmergencyCall) {
1053             mIsInEmergencyCall = false;
1054             String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
1055             if (Phone.DEBUG_PHONE) {
1056                 log("checkAndEnableDataCallAfterEmergencyCallDropped,inEcm=" + inEcm);
1057             }
1058             if (inEcm.compareTo("false") == 0) {
1059                 // Re-initiate data connection
1060                 // TODO - can this be changed to phone.enableDataConnectivity();
1061                 phone.mDataConnection.setDataEnabled(true);
1062             }
1063         }
1064     }
1065 
1066     /**
1067      * Check the MT call to see if it's a new ring or
1068      * a unknown connection.
1069      */
checkMtFindNewRinging(DriverCall dc, int i)1070     private Connection checkMtFindNewRinging(DriverCall dc, int i) {
1071 
1072         Connection newRinging = null;
1073 
1074         connections[i] = new CdmaConnection(phone.getContext(), dc, this, i);
1075         // it's a ringing call
1076         if (connections[i].getCall() == ringingCall) {
1077             newRinging = connections[i];
1078             if (Phone.DEBUG_PHONE) log("Notify new ring " + dc);
1079         } else {
1080             // Something strange happened: a call which is neither
1081             // a ringing call nor the one we created. It could be the
1082             // call collision result from RIL
1083             Log.e(LOG_TAG,"Phantom call appeared " + dc);
1084             // If it's a connected call, set the connect time so that
1085             // it's non-zero.  It may not be accurate, but at least
1086             // it won't appear as a Missed Call.
1087             if (dc.state != DriverCall.State.ALERTING
1088                 && dc.state != DriverCall.State.DIALING) {
1089                 connections[i].connectTime = System.currentTimeMillis();
1090             }
1091         }
1092         return newRinging;
1093     }
1094 
1095     /**
1096      * Check if current call is in emergency call
1097      *
1098      * @return true if it is in emergency call
1099      *         false if it is not in emergency call
1100      */
isInEmergencyCall()1101     boolean isInEmergencyCall() {
1102         return mIsInEmergencyCall;
1103     }
1104 
log(String msg)1105     protected void log(String msg) {
1106         Log.d(LOG_TAG, "[CdmaCallTracker] " + msg);
1107     }
1108 
1109 }
1110