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