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