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