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 import android.content.Context;
19 import android.os.AsyncResult;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.os.Message;
23 import android.os.PersistableBundle;
24 import android.os.PowerManager;
25 import android.os.Registrant;
26 import android.os.SystemClock;
27 import android.telephony.CarrierConfigManager;
28 import android.telephony.DisconnectCause;
29 import android.telephony.PhoneNumberUtils;
30 import android.telephony.Rlog;
31 import android.telephony.ServiceState;
32 import android.text.TextUtils;
33 
34 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
35 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
36 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
37 import com.android.internal.telephony.uicc.UiccCardApplication;
38 
39 /**
40  * {@hide}
41  */
42 public class GsmCdmaConnection extends Connection {
43     private static final String LOG_TAG = "GsmCdmaConnection";
44     private static final boolean DBG = true;
45     private static final boolean VDBG = false;
46 
47     //***** Instance Variables
48 
49     GsmCdmaCallTracker mOwner;
50     GsmCdmaCall mParent;
51 
52     boolean mDisconnected;
53 
54     int mIndex;          // index in GsmCdmaCallTracker.connections[], -1 if unassigned
55                         // The GsmCdma index is 1 + this
56 
57     /*
58      * These time/timespan values are based on System.currentTimeMillis(),
59      * i.e., "wall clock" time.
60      */
61     long mDisconnectTime;
62 
63     UUSInfo mUusInfo;
64     int mPreciseCause = 0;
65     String mVendorCause;
66 
67     Connection mOrigConnection;
68 
69     Handler mHandler;
70 
71     private PowerManager.WakeLock mPartialWakeLock;
72 
73     private boolean mIsEmergencyCall = false;
74 
75     // The cached delay to be used between DTMF tones fetched from carrier config.
76     private int mDtmfToneDelay = 0;
77 
78     //***** Event Constants
79     static final int EVENT_DTMF_DONE = 1;
80     static final int EVENT_PAUSE_DONE = 2;
81     static final int EVENT_NEXT_POST_DIAL = 3;
82     static final int EVENT_WAKE_LOCK_TIMEOUT = 4;
83     static final int EVENT_DTMF_DELAY_DONE = 5;
84 
85     //***** Constants
86     static final int PAUSE_DELAY_MILLIS_GSM = 3 * 1000;
87     static final int PAUSE_DELAY_MILLIS_CDMA = 2 * 1000;
88     static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000;
89 
90     //***** Inner Classes
91 
92     class MyHandler extends Handler {
MyHandler(Looper l)93         MyHandler(Looper l) {super(l);}
94 
95         @Override
96         public void
handleMessage(Message msg)97         handleMessage(Message msg) {
98 
99             switch (msg.what) {
100                 case EVENT_NEXT_POST_DIAL:
101                 case EVENT_DTMF_DELAY_DONE:
102                 case EVENT_PAUSE_DONE:
103                     processNextPostDialChar();
104                     break;
105                 case EVENT_WAKE_LOCK_TIMEOUT:
106                     releaseWakeLock();
107                     break;
108                 case EVENT_DTMF_DONE:
109                     // We may need to add a delay specified by carrier between DTMF tones that are
110                     // sent out.
111                     mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_DTMF_DELAY_DONE),
112                             mDtmfToneDelay);
113                     break;
114             }
115         }
116     }
117 
118     //***** Constructors
119 
120     /** This is probably an MT call that we first saw in a CLCC response or a hand over. */
GsmCdmaConnection(GsmCdmaPhone phone, DriverCall dc, GsmCdmaCallTracker ct, int index)121     public GsmCdmaConnection (GsmCdmaPhone phone, DriverCall dc, GsmCdmaCallTracker ct, int index) {
122         super(phone.getPhoneType());
123         createWakeLock(phone.getContext());
124         acquireWakeLock();
125 
126         mOwner = ct;
127         mHandler = new MyHandler(mOwner.getLooper());
128 
129         mAddress = dc.number;
130         mIsEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(phone.getContext(), mAddress);
131         mIsIncoming = dc.isMT;
132         mCreateTime = System.currentTimeMillis();
133         mCnapName = dc.name;
134         mCnapNamePresentation = dc.namePresentation;
135         mNumberPresentation = dc.numberPresentation;
136         mUusInfo = dc.uusInfo;
137 
138         mIndex = index;
139 
140         mParent = parentFromDCState(dc.state);
141         mParent.attach(this, dc);
142 
143         fetchDtmfToneDelay(phone);
144 
145         setAudioQuality(getAudioQualityFromDC(dc.audioQuality));
146     }
147 
148     /** This is an MO call, created when dialing */
GsmCdmaConnection(GsmCdmaPhone phone, String dialString, GsmCdmaCallTracker ct, GsmCdmaCall parent, boolean isEmergencyCall)149     public GsmCdmaConnection (GsmCdmaPhone phone, String dialString, GsmCdmaCallTracker ct,
150                               GsmCdmaCall parent, boolean isEmergencyCall) {
151         super(phone.getPhoneType());
152         createWakeLock(phone.getContext());
153         acquireWakeLock();
154 
155         mOwner = ct;
156         mHandler = new MyHandler(mOwner.getLooper());
157 
158         if (isPhoneTypeGsm()) {
159             mDialString = dialString;
160         } else {
161             Rlog.d(LOG_TAG, "[GsmCdmaConn] GsmCdmaConnection: dialString=" +
162                     maskDialString(dialString));
163             dialString = formatDialString(dialString);
164             Rlog.d(LOG_TAG,
165                     "[GsmCdmaConn] GsmCdmaConnection:formated dialString=" +
166                             maskDialString(dialString));
167         }
168 
169         mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
170         mIsEmergencyCall = isEmergencyCall;
171         mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
172 
173         mIndex = -1;
174 
175         mIsIncoming = false;
176         mCnapName = null;
177         mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
178         mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
179         mCreateTime = System.currentTimeMillis();
180 
181         if (parent != null) {
182             mParent = parent;
183             if (isPhoneTypeGsm()) {
184                 parent.attachFake(this, GsmCdmaCall.State.DIALING);
185             } else {
186                 //for the three way call case, not change parent state
187                 if (parent.mState == GsmCdmaCall.State.ACTIVE) {
188                     parent.attachFake(this, GsmCdmaCall.State.ACTIVE);
189                 } else {
190                     parent.attachFake(this, GsmCdmaCall.State.DIALING);
191                 }
192 
193             }
194         }
195 
196         fetchDtmfToneDelay(phone);
197     }
198 
199     //CDMA
200     /** This is a Call waiting call*/
GsmCdmaConnection(Context context, CdmaCallWaitingNotification cw, GsmCdmaCallTracker ct, GsmCdmaCall parent)201     public GsmCdmaConnection(Context context, CdmaCallWaitingNotification cw, GsmCdmaCallTracker ct,
202                              GsmCdmaCall parent) {
203         super(parent.getPhone().getPhoneType());
204         createWakeLock(context);
205         acquireWakeLock();
206 
207         mOwner = ct;
208         mHandler = new MyHandler(mOwner.getLooper());
209         mAddress = cw.number;
210         mNumberPresentation = cw.numberPresentation;
211         mCnapName = cw.name;
212         mCnapNamePresentation = cw.namePresentation;
213         mIndex = -1;
214         mIsIncoming = true;
215         mCreateTime = System.currentTimeMillis();
216         mConnectTime = 0;
217         mParent = parent;
218         parent.attachFake(this, GsmCdmaCall.State.WAITING);
219     }
220 
221 
dispose()222     public void dispose() {
223         clearPostDialListeners();
224         if (mParent != null) {
225             mParent.detach(this);
226         }
227         releaseAllWakeLocks();
228     }
229 
equalsHandlesNulls(Object a, Object b)230     static boolean equalsHandlesNulls(Object a, Object b) {
231         return (a == null) ? (b == null) : a.equals (b);
232     }
233 
234     static boolean
equalsBaseDialString(String a, String b)235     equalsBaseDialString (String a, String b) {
236         return (a == null) ? (b == null) : (b != null && a.startsWith (b));
237     }
238 
239     //CDMA
240     /**
241      * format original dial string
242      * 1) convert international dialing prefix "+" to
243      *    string specified per region
244      *
245      * 2) handle corner cases for PAUSE/WAIT dialing:
246      *
247      *    If PAUSE/WAIT sequence at the end, ignore them.
248      *
249      *    If consecutive PAUSE/WAIT sequence in the middle of the string,
250      *    and if there is any WAIT in PAUSE/WAIT sequence, treat them like WAIT.
251      */
formatDialString(String phoneNumber)252     public static String formatDialString(String phoneNumber) {
253         /**
254          * TODO(cleanup): This function should move to PhoneNumberUtils, and
255          * tests should be added.
256          */
257 
258         if (phoneNumber == null) {
259             return null;
260         }
261         int length = phoneNumber.length();
262         StringBuilder ret = new StringBuilder();
263         char c;
264         int currIndex = 0;
265 
266         while (currIndex < length) {
267             c = phoneNumber.charAt(currIndex);
268             if (isPause(c) || isWait(c)) {
269                 if (currIndex < length - 1) {
270                     // if PW not at the end
271                     int nextIndex = findNextPCharOrNonPOrNonWCharIndex(phoneNumber, currIndex);
272                     // If there is non PW char following PW sequence
273                     if (nextIndex < length) {
274                         char pC = findPOrWCharToAppend(phoneNumber, currIndex, nextIndex);
275                         ret.append(pC);
276                         // If PW char sequence has more than 2 PW characters,
277                         // skip to the last PW character since the sequence already be
278                         // converted to WAIT character
279                         if (nextIndex > (currIndex + 1)) {
280                             currIndex = nextIndex - 1;
281                         }
282                     } else if (nextIndex == length) {
283                         // It means PW characters at the end, ignore
284                         currIndex = length - 1;
285                     }
286                 }
287             } else {
288                 ret.append(c);
289             }
290             currIndex++;
291         }
292         return PhoneNumberUtils.cdmaCheckAndProcessPlusCode(ret.toString());
293     }
294 
295     /*package*/ boolean
compareTo(DriverCall c)296     compareTo(DriverCall c) {
297         // On mobile originated (MO) calls, the phone number may have changed
298         // due to a SIM Toolkit call control modification.
299         //
300         // We assume we know when MO calls are created (since we created them)
301         // and therefore don't need to compare the phone number anyway.
302         if (! (mIsIncoming || c.isMT)) return true;
303 
304         // A new call appearing by SRVCC may have invalid number
305         //  if IMS service is not tightly coupled with cellular modem stack.
306         // Thus we prefer the preexisting handover connection instance.
307         if (isPhoneTypeGsm() && mOrigConnection != null) return true;
308 
309         // ... but we can compare phone numbers on MT calls, and we have
310         // no control over when they begin, so we might as well
311 
312         String cAddress = PhoneNumberUtils.stringFromStringAndTOA(c.number, c.TOA);
313         return mIsIncoming == c.isMT && equalsHandlesNulls(mAddress, cAddress);
314     }
315 
316     @Override
getOrigDialString()317     public String getOrigDialString(){
318         return mDialString;
319     }
320 
321     @Override
getCall()322     public GsmCdmaCall getCall() {
323         return mParent;
324     }
325 
326     @Override
getDisconnectTime()327     public long getDisconnectTime() {
328         return mDisconnectTime;
329     }
330 
331     @Override
getHoldDurationMillis()332     public long getHoldDurationMillis() {
333         if (getState() != GsmCdmaCall.State.HOLDING) {
334             // If not holding, return 0
335             return 0;
336         } else {
337             return SystemClock.elapsedRealtime() - mHoldingStartTime;
338         }
339     }
340 
341     @Override
getState()342     public GsmCdmaCall.State getState() {
343         if (mDisconnected) {
344             return GsmCdmaCall.State.DISCONNECTED;
345         } else {
346             return super.getState();
347         }
348     }
349 
350     @Override
hangup()351     public void hangup() throws CallStateException {
352         if (!mDisconnected) {
353             mOwner.hangup(this);
354         } else {
355             throw new CallStateException ("disconnected");
356         }
357     }
358 
359     @Override
deflect(String number)360     public void deflect(String number) throws CallStateException {
361         // Deflect is not supported.
362         throw new CallStateException ("deflect is not supported for CS");
363     }
364 
365     @Override
separate()366     public void separate() throws CallStateException {
367         if (!mDisconnected) {
368             mOwner.separate(this);
369         } else {
370             throw new CallStateException ("disconnected");
371         }
372     }
373 
374     @Override
proceedAfterWaitChar()375     public void proceedAfterWaitChar() {
376         if (mPostDialState != PostDialState.WAIT) {
377             Rlog.w(LOG_TAG, "GsmCdmaConnection.proceedAfterWaitChar(): Expected "
378                     + "getPostDialState() to be WAIT but was " + mPostDialState);
379             return;
380         }
381 
382         setPostDialState(PostDialState.STARTED);
383 
384         processNextPostDialChar();
385     }
386 
387     @Override
proceedAfterWildChar(String str)388     public void proceedAfterWildChar(String str) {
389         if (mPostDialState != PostDialState.WILD) {
390             Rlog.w(LOG_TAG, "GsmCdmaConnection.proceedAfterWaitChar(): Expected "
391                 + "getPostDialState() to be WILD but was " + mPostDialState);
392             return;
393         }
394 
395         setPostDialState(PostDialState.STARTED);
396 
397         // make a new postDialString, with the wild char replacement string
398         // at the beginning, followed by the remaining postDialString.
399 
400         StringBuilder buf = new StringBuilder(str);
401         buf.append(mPostDialString.substring(mNextPostDialChar));
402         mPostDialString = buf.toString();
403         mNextPostDialChar = 0;
404         if (Phone.DEBUG_PHONE) {
405             log("proceedAfterWildChar: new postDialString is " +
406                     mPostDialString);
407         }
408 
409         processNextPostDialChar();
410     }
411 
412     @Override
cancelPostDial()413     public void cancelPostDial() {
414         setPostDialState(PostDialState.CANCELLED);
415     }
416 
417     /**
418      * Called when this Connection is being hung up locally (eg, user pressed "end")
419      * Note that at this point, the hangup request has been dispatched to the radio
420      * but no response has yet been received so update() has not yet been called
421      */
422     void
onHangupLocal()423     onHangupLocal() {
424         mCause = DisconnectCause.LOCAL;
425         mPreciseCause = 0;
426         mVendorCause = null;
427     }
428 
429     /**
430      * Maps RIL call disconnect code to {@link DisconnectCause}.
431      * @param causeCode RIL disconnect code
432      * @return the corresponding value from {@link DisconnectCause}
433      */
disconnectCauseFromCode(int causeCode)434     int disconnectCauseFromCode(int causeCode) {
435         /**
436          * See 22.001 Annex F.4 for mapping of cause codes
437          * to local tones
438          */
439 
440         switch (causeCode) {
441             case CallFailCause.USER_BUSY:
442                 return DisconnectCause.BUSY;
443 
444             case CallFailCause.NO_CIRCUIT_AVAIL:
445             case CallFailCause.TEMPORARY_FAILURE:
446             case CallFailCause.SWITCHING_CONGESTION:
447             case CallFailCause.CHANNEL_NOT_AVAIL:
448             case CallFailCause.QOS_NOT_AVAIL:
449             case CallFailCause.BEARER_NOT_AVAIL:
450                 return DisconnectCause.CONGESTION;
451 
452             case CallFailCause.EMERGENCY_TEMP_FAILURE:
453                 return DisconnectCause.EMERGENCY_TEMP_FAILURE;
454             case CallFailCause.EMERGENCY_PERM_FAILURE:
455                 return DisconnectCause.EMERGENCY_PERM_FAILURE;
456 
457             case CallFailCause.ACM_LIMIT_EXCEEDED:
458                 return DisconnectCause.LIMIT_EXCEEDED;
459 
460             case CallFailCause.OPERATOR_DETERMINED_BARRING:
461             case CallFailCause.CALL_BARRED:
462                 return DisconnectCause.CALL_BARRED;
463 
464             case CallFailCause.FDN_BLOCKED:
465                 return DisconnectCause.FDN_BLOCKED;
466 
467             case CallFailCause.IMEI_NOT_ACCEPTED:
468                 return DisconnectCause.IMEI_NOT_ACCEPTED;
469 
470             case CallFailCause.UNOBTAINABLE_NUMBER:
471                 return DisconnectCause.UNOBTAINABLE_NUMBER;
472 
473             case CallFailCause.DIAL_MODIFIED_TO_USSD:
474                 return DisconnectCause.DIAL_MODIFIED_TO_USSD;
475 
476             case CallFailCause.DIAL_MODIFIED_TO_SS:
477                 return DisconnectCause.DIAL_MODIFIED_TO_SS;
478 
479             case CallFailCause.DIAL_MODIFIED_TO_DIAL:
480                 return DisconnectCause.DIAL_MODIFIED_TO_DIAL;
481 
482             case CallFailCause.CDMA_LOCKED_UNTIL_POWER_CYCLE:
483                 return DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE;
484 
485             case CallFailCause.CDMA_DROP:
486                 return DisconnectCause.CDMA_DROP;
487 
488             case CallFailCause.CDMA_INTERCEPT:
489                 return DisconnectCause.CDMA_INTERCEPT;
490 
491             case CallFailCause.CDMA_REORDER:
492                 return DisconnectCause.CDMA_REORDER;
493 
494             case CallFailCause.CDMA_SO_REJECT:
495                 return DisconnectCause.CDMA_SO_REJECT;
496 
497             case CallFailCause.CDMA_RETRY_ORDER:
498                 return DisconnectCause.CDMA_RETRY_ORDER;
499 
500             case CallFailCause.CDMA_ACCESS_FAILURE:
501                 return DisconnectCause.CDMA_ACCESS_FAILURE;
502 
503             case CallFailCause.CDMA_PREEMPTED:
504                 return DisconnectCause.CDMA_PREEMPTED;
505 
506             case CallFailCause.CDMA_NOT_EMERGENCY:
507                 return DisconnectCause.CDMA_NOT_EMERGENCY;
508 
509             case CallFailCause.CDMA_ACCESS_BLOCKED:
510                 return DisconnectCause.CDMA_ACCESS_BLOCKED;
511 
512             case CallFailCause.NORMAL_UNSPECIFIED:
513                 return DisconnectCause.NORMAL_UNSPECIFIED;
514 
515             case CallFailCause.USER_ALERTING_NO_ANSWER:
516                 return DisconnectCause.TIMED_OUT;
517 
518             case CallFailCause.ERROR_UNSPECIFIED:
519             case CallFailCause.NORMAL_CLEARING:
520             default:
521                 GsmCdmaPhone phone = mOwner.getPhone();
522                 int serviceState = phone.getServiceState().getState();
523                 UiccCardApplication cardApp = phone.getUiccCardApplication();
524                 AppState uiccAppState = (cardApp != null) ? cardApp.getState() :
525                         AppState.APPSTATE_UNKNOWN;
526                 if (serviceState == ServiceState.STATE_POWER_OFF) {
527                     return DisconnectCause.POWER_OFF;
528                 }
529                 if (!mIsEmergencyCall) {
530                     // Only send OUT_OF_SERVICE if it is not an emergency call. We can still
531                     // technically be in STATE_OUT_OF_SERVICE or STATE_EMERGENCY_ONLY during
532                     // an emergency call and when it ends, we do not want to mistakenly generate
533                     // an OUT_OF_SERVICE disconnect cause during normal call ending.
534                     if ((serviceState == ServiceState.STATE_OUT_OF_SERVICE
535                             || serviceState == ServiceState.STATE_EMERGENCY_ONLY)) {
536                         return DisconnectCause.OUT_OF_SERVICE;
537                     }
538                     // If we are placing an emergency call and the SIM is currently PIN/PUK
539                     // locked the AppState will always not be equal to APPSTATE_READY.
540                     if (uiccAppState != AppState.APPSTATE_READY) {
541                         if (isPhoneTypeGsm()) {
542                             return DisconnectCause.ICC_ERROR;
543                         } else { // CDMA
544                             if (phone.mCdmaSubscriptionSource ==
545                                     CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM) {
546                                 return DisconnectCause.ICC_ERROR;
547                             }
548                         }
549                     }
550                 }
551                 if (isPhoneTypeGsm()) {
552                     if (causeCode == CallFailCause.ERROR_UNSPECIFIED) {
553                         if (phone.mSST.mRestrictedState.isCsRestricted()) {
554                             return DisconnectCause.CS_RESTRICTED;
555                         } else if (phone.mSST.mRestrictedState.isCsEmergencyRestricted()) {
556                             return DisconnectCause.CS_RESTRICTED_EMERGENCY;
557                         } else if (phone.mSST.mRestrictedState.isCsNormalRestricted()) {
558                             return DisconnectCause.CS_RESTRICTED_NORMAL;
559                         }
560                     }
561                 }
562                 if (causeCode == CallFailCause.NORMAL_CLEARING) {
563                     return DisconnectCause.NORMAL;
564                 }
565                 // If nothing else matches, report unknown call drop reason
566                 // to app, not NORMAL call end.
567                 return DisconnectCause.ERROR_UNSPECIFIED;
568         }
569     }
570 
571     /*package*/ void
onRemoteDisconnect(int causeCode, String vendorCause)572     onRemoteDisconnect(int causeCode, String vendorCause) {
573         this.mPreciseCause = causeCode;
574         this.mVendorCause = vendorCause;
575         onDisconnect(disconnectCauseFromCode(causeCode));
576     }
577 
578     /**
579      * Called when the radio indicates the connection has been disconnected.
580      * @param cause call disconnect cause; values are defined in {@link DisconnectCause}
581      */
582     @Override
onDisconnect(int cause)583     public boolean onDisconnect(int cause) {
584         boolean changed = false;
585 
586         mCause = cause;
587 
588         if (!mDisconnected) {
589             doDisconnect();
590 
591             if (DBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
592 
593             mOwner.getPhone().notifyDisconnect(this);
594             notifyDisconnect(cause);
595 
596             if (mParent != null) {
597                 changed = mParent.connectionDisconnected(this);
598             }
599 
600             mOrigConnection = null;
601         }
602         clearPostDialListeners();
603         releaseWakeLock();
604         return changed;
605     }
606 
607     //CDMA
608     /** Called when the call waiting connection has been hung up */
609     /*package*/ void
onLocalDisconnect()610     onLocalDisconnect() {
611         if (!mDisconnected) {
612             doDisconnect();
613             if (VDBG) Rlog.d(LOG_TAG, "onLoalDisconnect" );
614 
615             if (mParent != null) {
616                 mParent.detach(this);
617             }
618         }
619         releaseWakeLock();
620     }
621 
622     // Returns true if state has changed, false if nothing changed
623     public boolean
update(DriverCall dc)624     update (DriverCall dc) {
625         GsmCdmaCall newParent;
626         boolean changed = false;
627         boolean wasConnectingInOrOut = isConnectingInOrOut();
628         boolean wasHolding = (getState() == GsmCdmaCall.State.HOLDING);
629 
630         newParent = parentFromDCState(dc.state);
631 
632         if (Phone.DEBUG_PHONE) log("parent= " +mParent +", newParent= " + newParent);
633 
634         //Ignore dc.number and dc.name in case of a handover connection
635         if (isPhoneTypeGsm() && mOrigConnection != null) {
636             if (Phone.DEBUG_PHONE) log("update: mOrigConnection is not null");
637         } else if (isIncoming()) {
638             if (!equalsBaseDialString(mAddress, dc.number) && (!mNumberConverted
639                     || !equalsBaseDialString(mConvertedNumber, dc.number))) {
640                 if (Phone.DEBUG_PHONE) log("update: phone # changed!");
641                 mAddress = dc.number;
642                 changed = true;
643             }
644         }
645 
646         int newAudioQuality = getAudioQualityFromDC(dc.audioQuality);
647         if (getAudioQuality() != newAudioQuality) {
648             if (Phone.DEBUG_PHONE) {
649                 log("update: audioQuality # changed!:  "
650                         + (newAudioQuality == Connection.AUDIO_QUALITY_HIGH_DEFINITION
651                         ? "high" : "standard"));
652             }
653             setAudioQuality(newAudioQuality);
654             changed = true;
655         }
656 
657         // A null cnapName should be the same as ""
658         if (TextUtils.isEmpty(dc.name)) {
659             if (!TextUtils.isEmpty(mCnapName)) {
660                 changed = true;
661                 mCnapName = "";
662             }
663         } else if (!dc.name.equals(mCnapName)) {
664             changed = true;
665             mCnapName = dc.name;
666         }
667 
668         if (Phone.DEBUG_PHONE) log("--dssds----"+mCnapName);
669         mCnapNamePresentation = dc.namePresentation;
670         mNumberPresentation = dc.numberPresentation;
671 
672         if (newParent != mParent) {
673             if (mParent != null) {
674                 mParent.detach(this);
675             }
676             newParent.attach(this, dc);
677             mParent = newParent;
678             changed = true;
679         } else {
680             boolean parentStateChange;
681             parentStateChange = mParent.update (this, dc);
682             changed = changed || parentStateChange;
683         }
684 
685         /** Some state-transition events */
686 
687         if (Phone.DEBUG_PHONE) log(
688                 "update: parent=" + mParent +
689                 ", hasNewParent=" + (newParent != mParent) +
690                 ", wasConnectingInOrOut=" + wasConnectingInOrOut +
691                 ", wasHolding=" + wasHolding +
692                 ", isConnectingInOrOut=" + isConnectingInOrOut() +
693                 ", changed=" + changed);
694 
695 
696         if (wasConnectingInOrOut && !isConnectingInOrOut()) {
697             onConnectedInOrOut();
698         }
699 
700         if (changed && !wasHolding && (getState() == GsmCdmaCall.State.HOLDING)) {
701             // We've transitioned into HOLDING
702             onStartedHolding();
703         }
704 
705         return changed;
706     }
707 
708     /**
709      * Called when this Connection is in the foregroundCall
710      * when a dial is initiated.
711      * We know we're ACTIVE, and we know we're going to end up
712      * HOLDING in the backgroundCall
713      */
714     void
fakeHoldBeforeDial()715     fakeHoldBeforeDial() {
716         if (mParent != null) {
717             mParent.detach(this);
718         }
719 
720         mParent = mOwner.mBackgroundCall;
721         mParent.attachFake(this, GsmCdmaCall.State.HOLDING);
722 
723         onStartedHolding();
724     }
725 
726     /*package*/ int
getGsmCdmaIndex()727     getGsmCdmaIndex() throws CallStateException {
728         if (mIndex >= 0) {
729             return mIndex + 1;
730         } else {
731             throw new CallStateException ("GsmCdma index not yet assigned");
732         }
733     }
734 
735     /**
736      * An incoming or outgoing call has connected
737      */
738     void
onConnectedInOrOut()739     onConnectedInOrOut() {
740         mConnectTime = System.currentTimeMillis();
741         mConnectTimeReal = SystemClock.elapsedRealtime();
742         mDuration = 0;
743 
744         // bug #678474: incoming call interpreted as missed call, even though
745         // it sounds like the user has picked up the call.
746         if (Phone.DEBUG_PHONE) {
747             log("onConnectedInOrOut: connectTime=" + mConnectTime);
748         }
749 
750         if (!mIsIncoming) {
751             // outgoing calls only
752             processNextPostDialChar();
753         } else {
754             // Only release wake lock for incoming calls, for outgoing calls the wake lock
755             // will be released after any pause-dial is completed
756             releaseWakeLock();
757         }
758     }
759 
760     private void
doDisconnect()761     doDisconnect() {
762         mIndex = -1;
763         mDisconnectTime = System.currentTimeMillis();
764         mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;
765         mDisconnected = true;
766         clearPostDialListeners();
767     }
768 
769     /*package*/ void
onStartedHolding()770     onStartedHolding() {
771         mHoldingStartTime = SystemClock.elapsedRealtime();
772     }
773 
774     /**
775      * Performs the appropriate action for a post-dial char, but does not
776      * notify application. returns false if the character is invalid and
777      * should be ignored
778      */
779     private boolean
processPostDialChar(char c)780     processPostDialChar(char c) {
781         if (PhoneNumberUtils.is12Key(c)) {
782             mOwner.mCi.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
783         } else if (isPause(c)) {
784             if (!isPhoneTypeGsm()) {
785                 setPostDialState(PostDialState.PAUSE);
786             }
787             // From TS 22.101:
788             // It continues...
789             // Upon the called party answering the UE shall send the DTMF digits
790             // automatically to the network after a delay of 3 seconds( 20 ).
791             // The digits shall be sent according to the procedures and timing
792             // specified in 3GPP TS 24.008 [13]. The first occurrence of the
793             // "DTMF Control Digits Separator" shall be used by the ME to
794             // distinguish between the addressing digits (i.e. the phone number)
795             // and the DTMF digits. Upon subsequent occurrences of the
796             // separator,
797             // the UE shall pause again for 3 seconds ( 20 ) before sending
798             // any further DTMF digits.
799             mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_PAUSE_DONE),
800                     isPhoneTypeGsm() ? PAUSE_DELAY_MILLIS_GSM: PAUSE_DELAY_MILLIS_CDMA);
801         } else if (isWait(c)) {
802             setPostDialState(PostDialState.WAIT);
803         } else if (isWild(c)) {
804             setPostDialState(PostDialState.WILD);
805         } else {
806             return false;
807         }
808 
809         return true;
810     }
811 
812     @Override
813     public String
getRemainingPostDialString()814     getRemainingPostDialString() {
815         String subStr = super.getRemainingPostDialString();
816         if (!isPhoneTypeGsm() && !TextUtils.isEmpty(subStr)) {
817             int wIndex = subStr.indexOf(PhoneNumberUtils.WAIT);
818             int pIndex = subStr.indexOf(PhoneNumberUtils.PAUSE);
819 
820             if (wIndex > 0 && (wIndex < pIndex || pIndex <= 0)) {
821                 subStr = subStr.substring(0, wIndex);
822             } else if (pIndex > 0) {
823                 subStr = subStr.substring(0, pIndex);
824             }
825         }
826         return subStr;
827     }
828 
829     //CDMA
updateParent(GsmCdmaCall oldParent, GsmCdmaCall newParent)830     public void updateParent(GsmCdmaCall oldParent, GsmCdmaCall newParent){
831         if (newParent != oldParent) {
832             if (oldParent != null) {
833                 oldParent.detach(this);
834             }
835             newParent.attachFake(this, GsmCdmaCall.State.ACTIVE);
836             mParent = newParent;
837         }
838     }
839 
840     @Override
finalize()841     protected void finalize()
842     {
843         /**
844          * It is understood that This finalizer is not guaranteed
845          * to be called and the release lock call is here just in
846          * case there is some path that doesn't call onDisconnect
847          * and or onConnectedInOrOut.
848          */
849         if (mPartialWakeLock != null && mPartialWakeLock.isHeld()) {
850             Rlog.e(LOG_TAG, "UNEXPECTED; mPartialWakeLock is held when finalizing.");
851         }
852         clearPostDialListeners();
853         releaseWakeLock();
854     }
855 
856     private void
processNextPostDialChar()857     processNextPostDialChar() {
858         char c = 0;
859         Registrant postDialHandler;
860 
861         if (mPostDialState == PostDialState.CANCELLED) {
862             releaseWakeLock();
863             return;
864         }
865 
866         if (mPostDialString == null ||
867                 mPostDialString.length() <= mNextPostDialChar) {
868             setPostDialState(PostDialState.COMPLETE);
869 
870             // We were holding a wake lock until pause-dial was complete, so give it up now
871             releaseWakeLock();
872 
873             // notifyMessage.arg1 is 0 on complete
874             c = 0;
875         } else {
876             boolean isValid;
877 
878             setPostDialState(PostDialState.STARTED);
879 
880             c = mPostDialString.charAt(mNextPostDialChar++);
881 
882             isValid = processPostDialChar(c);
883 
884             if (!isValid) {
885                 // Will call processNextPostDialChar
886                 mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget();
887                 // Don't notify application
888                 Rlog.e(LOG_TAG, "processNextPostDialChar: c=" + c + " isn't valid!");
889                 return;
890             }
891         }
892 
893         notifyPostDialListenersNextChar(c);
894 
895         // TODO: remove the following code since the handler no longer executes anything.
896         postDialHandler = mOwner.getPhone().getPostDialHandler();
897 
898         Message notifyMessage;
899 
900         if (postDialHandler != null
901                 && (notifyMessage = postDialHandler.messageForRegistrant()) != null) {
902             // The AsyncResult.result is the Connection object
903             PostDialState state = mPostDialState;
904             AsyncResult ar = AsyncResult.forMessage(notifyMessage);
905             ar.result = this;
906             ar.userObj = state;
907 
908             // arg1 is the character that was/is being processed
909             notifyMessage.arg1 = c;
910 
911             //Rlog.v("GsmCdma", "##### processNextPostDialChar: send msg to postDialHandler, arg1=" + c);
912             notifyMessage.sendToTarget();
913         }
914     }
915 
916     /** "connecting" means "has never been ACTIVE" for both incoming
917      *  and outgoing calls
918      */
919     private boolean
isConnectingInOrOut()920     isConnectingInOrOut() {
921         return mParent == null || mParent == mOwner.mRingingCall
922             || mParent.mState == GsmCdmaCall.State.DIALING
923             || mParent.mState == GsmCdmaCall.State.ALERTING;
924     }
925 
926     private GsmCdmaCall
parentFromDCState(DriverCall.State state)927     parentFromDCState (DriverCall.State state) {
928         switch (state) {
929             case ACTIVE:
930             case DIALING:
931             case ALERTING:
932                 return mOwner.mForegroundCall;
933             //break;
934 
935             case HOLDING:
936                 return mOwner.mBackgroundCall;
937             //break;
938 
939             case INCOMING:
940             case WAITING:
941                 return mOwner.mRingingCall;
942             //break;
943 
944             default:
945                 throw new RuntimeException("illegal call state: " + state);
946         }
947     }
948 
getAudioQualityFromDC(int audioQuality)949     private int getAudioQualityFromDC(int audioQuality) {
950         switch (audioQuality) {
951             case DriverCall.AUDIO_QUALITY_AMR_WB:
952             case DriverCall.AUDIO_QUALITY_EVRC_NW:
953                 return Connection.AUDIO_QUALITY_HIGH_DEFINITION;
954             default:
955                 return Connection.AUDIO_QUALITY_STANDARD;
956         }
957     }
958 
959     /**
960      * Set post dial state and acquire wake lock while switching to "started" or "pause"
961      * state, the wake lock will be released if state switches out of "started" or "pause"
962      * state or after WAKE_LOCK_TIMEOUT_MILLIS.
963      * @param s new PostDialState
964      */
setPostDialState(PostDialState s)965     private void setPostDialState(PostDialState s) {
966         if (s == PostDialState.STARTED ||
967                 s == PostDialState.PAUSE) {
968             synchronized (mPartialWakeLock) {
969                 if (mPartialWakeLock.isHeld()) {
970                     mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
971                 } else {
972                     acquireWakeLock();
973                 }
974                 Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
975                 mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS);
976             }
977         } else {
978             mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
979             releaseWakeLock();
980         }
981         mPostDialState = s;
982         notifyPostDialListeners();
983     }
984 
createWakeLock(Context context)985     private void createWakeLock(Context context) {
986         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
987         mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
988     }
989 
acquireWakeLock()990     private void acquireWakeLock() {
991         if (mPartialWakeLock != null) {
992             synchronized (mPartialWakeLock) {
993                 log("acquireWakeLock");
994                 mPartialWakeLock.acquire();
995             }
996         }
997     }
998 
releaseWakeLock()999     private void releaseWakeLock() {
1000         if (mPartialWakeLock != null) {
1001             synchronized (mPartialWakeLock) {
1002                 if (mPartialWakeLock.isHeld()) {
1003                     log("releaseWakeLock");
1004                     mPartialWakeLock.release();
1005                 }
1006             }
1007         }
1008     }
1009 
releaseAllWakeLocks()1010     private void releaseAllWakeLocks() {
1011         if (mPartialWakeLock != null) {
1012             synchronized (mPartialWakeLock) {
1013                 while (mPartialWakeLock.isHeld()) {
1014                     mPartialWakeLock.release();
1015                 }
1016             }
1017         }
1018     }
1019 
isPause(char c)1020     private static boolean isPause(char c) {
1021         return c == PhoneNumberUtils.PAUSE;
1022     }
1023 
isWait(char c)1024     private static boolean isWait(char c) {
1025         return c == PhoneNumberUtils.WAIT;
1026     }
1027 
isWild(char c)1028     private static boolean isWild(char c) {
1029         return c == PhoneNumberUtils.WILD;
1030     }
1031 
1032     //CDMA
1033     // This function is to find the next PAUSE character index if
1034     // multiple pauses in a row. Otherwise it finds the next non PAUSE or
1035     // non WAIT character index.
findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex)1036     private static int findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex) {
1037         boolean wMatched = isWait(phoneNumber.charAt(currIndex));
1038         int index = currIndex + 1;
1039         int length = phoneNumber.length();
1040         while (index < length) {
1041             char cNext = phoneNumber.charAt(index);
1042             // if there is any W inside P/W sequence,mark it
1043             if (isWait(cNext)) {
1044                 wMatched = true;
1045             }
1046             // if any characters other than P/W chars after P/W sequence
1047             // we break out the loop and append the correct
1048             if (!isWait(cNext) && !isPause(cNext)) {
1049                 break;
1050             }
1051             index++;
1052         }
1053 
1054         // It means the PAUSE character(s) is in the middle of dial string
1055         // and it needs to be handled one by one.
1056         if ((index < length) && (index > (currIndex + 1))  &&
1057                 ((wMatched == false) && isPause(phoneNumber.charAt(currIndex)))) {
1058             return (currIndex + 1);
1059         }
1060         return index;
1061     }
1062 
1063     // CDMA
1064     // This function returns either PAUSE or WAIT character to append.
1065     // It is based on the next non PAUSE/WAIT character in the phoneNumber and the
1066     // index for the current PAUSE/WAIT character
findPOrWCharToAppend(String phoneNumber, int currPwIndex, int nextNonPwCharIndex)1067     private static char findPOrWCharToAppend(String phoneNumber, int currPwIndex,
1068                                              int nextNonPwCharIndex) {
1069         char c = phoneNumber.charAt(currPwIndex);
1070         char ret;
1071 
1072         // Append the PW char
1073         ret = (isPause(c)) ? PhoneNumberUtils.PAUSE : PhoneNumberUtils.WAIT;
1074 
1075         // If the nextNonPwCharIndex is greater than currPwIndex + 1,
1076         // it means the PW sequence contains not only P characters.
1077         // Since for the sequence that only contains P character,
1078         // the P character is handled one by one, the nextNonPwCharIndex
1079         // equals to currPwIndex + 1.
1080         // In this case, skip P, append W.
1081         if (nextNonPwCharIndex > (currPwIndex + 1)) {
1082             ret = PhoneNumberUtils.WAIT;
1083         }
1084         return ret;
1085     }
1086 
maskDialString(String dialString)1087     private String maskDialString(String dialString) {
1088         if (VDBG) {
1089             return dialString;
1090         }
1091 
1092         return "<MASKED>";
1093     }
1094 
fetchDtmfToneDelay(GsmCdmaPhone phone)1095     private void fetchDtmfToneDelay(GsmCdmaPhone phone) {
1096         CarrierConfigManager configMgr = (CarrierConfigManager)
1097                 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
1098         PersistableBundle b = configMgr.getConfigForSubId(phone.getSubId());
1099         if (b != null) {
1100             mDtmfToneDelay = b.getInt(phone.getDtmfToneDelayKey());
1101         }
1102     }
1103 
isPhoneTypeGsm()1104     private boolean isPhoneTypeGsm() {
1105         return mOwner.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_GSM;
1106     }
1107 
log(String msg)1108     private void log(String msg) {
1109         Rlog.d(LOG_TAG, "[GsmCdmaConn] " + msg);
1110     }
1111 
1112     @Override
getNumberPresentation()1113     public int getNumberPresentation() {
1114         return mNumberPresentation;
1115     }
1116 
1117     @Override
getUUSInfo()1118     public UUSInfo getUUSInfo() {
1119         return mUusInfo;
1120     }
1121 
getPreciseDisconnectCause()1122     public int getPreciseDisconnectCause() {
1123         return mPreciseCause;
1124     }
1125 
1126     @Override
getVendorDisconnectCause()1127     public String getVendorDisconnectCause() {
1128         return mVendorCause;
1129     }
1130 
1131     @Override
migrateFrom(Connection c)1132     public void migrateFrom(Connection c) {
1133         if (c == null) return;
1134 
1135         super.migrateFrom(c);
1136 
1137         this.mUusInfo = c.getUUSInfo();
1138 
1139         this.setUserData(c.getUserData());
1140     }
1141 
1142     @Override
getOrigConnection()1143     public Connection getOrigConnection() {
1144         return mOrigConnection;
1145     }
1146 
1147     @Override
isMultiparty()1148     public boolean isMultiparty() {
1149         if (mOrigConnection != null) {
1150             return mOrigConnection.isMultiparty();
1151         }
1152 
1153         return false;
1154     }
1155 }
1156