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);
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);
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);
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     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.ACCESS_CLASS_BLOCKED:
569             case CallFailCause.ERROR_UNSPECIFIED:
570             case CallFailCause.NORMAL_CLEARING:
571             default:
572                 GsmCdmaPhone phone = mOwner.getPhone();
573                 int serviceState = phone.getServiceState().getState();
574                 UiccCardApplication cardApp = phone.getUiccCardApplication();
575                 AppState uiccAppState = (cardApp != null) ? cardApp.getState() :
576                         AppState.APPSTATE_UNKNOWN;
577                 if (serviceState == ServiceState.STATE_POWER_OFF) {
578                     return DisconnectCause.POWER_OFF;
579                 }
580                 if (!isEmergencyCall()) {
581                     // Only send OUT_OF_SERVICE if it is not an emergency call. We can still
582                     // technically be in STATE_OUT_OF_SERVICE or STATE_EMERGENCY_ONLY during
583                     // an emergency call and when it ends, we do not want to mistakenly generate
584                     // an OUT_OF_SERVICE disconnect cause during normal call ending.
585                     if ((serviceState == ServiceState.STATE_OUT_OF_SERVICE
586                             || serviceState == ServiceState.STATE_EMERGENCY_ONLY)) {
587                         return DisconnectCause.OUT_OF_SERVICE;
588                     }
589                     // If we are placing an emergency call and the SIM is currently PIN/PUK
590                     // locked the AppState will always not be equal to APPSTATE_READY.
591                     if (uiccAppState != AppState.APPSTATE_READY) {
592                         if (isPhoneTypeGsm()) {
593                             return DisconnectCause.ICC_ERROR;
594                         } else { // CDMA
595                             if (phone.mCdmaSubscriptionSource ==
596                                     CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM) {
597                                 return DisconnectCause.ICC_ERROR;
598                             }
599                         }
600                     }
601                 }
602                 if (isPhoneTypeGsm()) {
603                     if (causeCode == CallFailCause.ERROR_UNSPECIFIED ||
604                                    causeCode == CallFailCause.ACCESS_CLASS_BLOCKED ) {
605                         if (phone.mSST.mRestrictedState.isCsRestricted()) {
606                             return DisconnectCause.CS_RESTRICTED;
607                         } else if (phone.mSST.mRestrictedState.isCsEmergencyRestricted()) {
608                             return DisconnectCause.CS_RESTRICTED_EMERGENCY;
609                         } else if (phone.mSST.mRestrictedState.isCsNormalRestricted()) {
610                             return DisconnectCause.CS_RESTRICTED_NORMAL;
611                         }
612                     }
613                 }
614                 if (causeCode == CallFailCause.NORMAL_CLEARING) {
615                     return DisconnectCause.NORMAL;
616                 }
617                 // If nothing else matches, report unknown call drop reason
618                 // to app, not NORMAL call end.
619                 return DisconnectCause.ERROR_UNSPECIFIED;
620         }
621     }
622 
623     /*package*/ void
onRemoteDisconnect(int causeCode, String vendorCause)624     onRemoteDisconnect(int causeCode, String vendorCause) {
625         this.mPreciseCause = causeCode;
626         this.mVendorCause = vendorCause;
627         onDisconnect(disconnectCauseFromCode(causeCode));
628     }
629 
630     /**
631      * Called when the radio indicates the connection has been disconnected.
632      * @param cause call disconnect cause; values are defined in {@link DisconnectCause}
633      */
634     @Override
onDisconnect(int cause)635     public boolean onDisconnect(int cause) {
636         boolean changed = false;
637 
638         mCause = cause;
639 
640         if (!mDisconnected) {
641             doDisconnect();
642 
643             if (DBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
644 
645             mOwner.getPhone().notifyDisconnect(this);
646             notifyDisconnect(cause);
647 
648             if (mParent != null) {
649                 changed = mParent.connectionDisconnected(this);
650             }
651 
652             mOrigConnection = null;
653         }
654         clearPostDialListeners();
655         releaseWakeLock();
656         return changed;
657     }
658 
659     //CDMA
660     /** Called when the call waiting connection has been hung up */
661     /*package*/ void
onLocalDisconnect()662     onLocalDisconnect() {
663         if (!mDisconnected) {
664             doDisconnect();
665             if (VDBG) Rlog.d(LOG_TAG, "onLoalDisconnect" );
666 
667             if (mParent != null) {
668                 mParent.detach(this);
669             }
670         }
671         releaseWakeLock();
672     }
673 
674     // Returns true if state has changed, false if nothing changed
675     public boolean
update(DriverCall dc)676     update (DriverCall dc) {
677         GsmCdmaCall newParent;
678         boolean changed = false;
679         boolean wasConnectingInOrOut = isConnectingInOrOut();
680         boolean wasHolding = (getState() == GsmCdmaCall.State.HOLDING);
681 
682         newParent = parentFromDCState(dc.state);
683 
684         if (Phone.DEBUG_PHONE) log("parent= " +mParent +", newParent= " + newParent);
685 
686         //Ignore dc.number and dc.name in case of a handover connection
687         if (isPhoneTypeGsm() && mOrigConnection != null) {
688             if (Phone.DEBUG_PHONE) log("update: mOrigConnection is not null");
689         } else if (isIncoming()) {
690             if (!equalsBaseDialString(mAddress, dc.number) && (!mNumberConverted
691                     || !equalsBaseDialString(mConvertedNumber, dc.number))) {
692                 if (Phone.DEBUG_PHONE) log("update: phone # changed!");
693                 mAddress = dc.number;
694                 changed = true;
695             }
696         }
697 
698         int newAudioQuality = getAudioQualityFromDC(dc.audioQuality);
699         if (getAudioQuality() != newAudioQuality) {
700             if (Phone.DEBUG_PHONE) {
701                 log("update: audioQuality # changed!:  "
702                         + (newAudioQuality == Connection.AUDIO_QUALITY_HIGH_DEFINITION
703                         ? "high" : "standard"));
704             }
705             setAudioQuality(newAudioQuality);
706             changed = true;
707         }
708 
709         // Metrics for audio codec
710         if (dc.audioQuality != mAudioCodec) {
711             mAudioCodec = dc.audioQuality;
712             mMetrics.writeAudioCodecGsmCdma(mOwner.getPhone().getPhoneId(), dc.audioQuality);
713             mOwner.getPhone().getVoiceCallSessionStats().onAudioCodecChanged(this, dc.audioQuality);
714         }
715 
716         String forwardedNumber = TextUtils.isEmpty(dc.forwardedNumber) ? null : dc.forwardedNumber;
717         Rlog.i(LOG_TAG, "update: forwardedNumber=" + Rlog.pii(LOG_TAG, forwardedNumber));
718         ArrayList<String> forwardedNumbers =  forwardedNumber == null ? null :
719                 new ArrayList<>(Collections.singletonList(dc.forwardedNumber));
720         if (!equalsHandlesNulls(mForwardedNumber, forwardedNumbers)) {
721             if (Phone.DEBUG_PHONE) log("update: mForwardedNumber, # changed");
722             mForwardedNumber = forwardedNumbers;
723             changed = true;
724         }
725 
726         // A null cnapName should be the same as ""
727         if (TextUtils.isEmpty(dc.name)) {
728             if (!TextUtils.isEmpty(mCnapName)) {
729                 changed = true;
730                 mCnapName = "";
731             }
732         } else if (!dc.name.equals(mCnapName)) {
733             changed = true;
734             mCnapName = dc.name;
735         }
736 
737         if (Phone.DEBUG_PHONE) log("--dssds----"+mCnapName);
738         mCnapNamePresentation = dc.namePresentation;
739         mNumberPresentation = dc.numberPresentation;
740 
741         if (newParent != mParent) {
742             if (mParent != null) {
743                 mParent.detach(this);
744             }
745             newParent.attach(this, dc);
746             mParent = newParent;
747             changed = true;
748         } else {
749             boolean parentStateChange;
750             parentStateChange = mParent.update (this, dc);
751             changed = changed || parentStateChange;
752         }
753 
754         /** Some state-transition events */
755 
756         if (Phone.DEBUG_PHONE) log(
757                 "update: parent=" + mParent +
758                 ", hasNewParent=" + (newParent != mParent) +
759                 ", wasConnectingInOrOut=" + wasConnectingInOrOut +
760                 ", wasHolding=" + wasHolding +
761                 ", isConnectingInOrOut=" + isConnectingInOrOut() +
762                 ", changed=" + changed);
763 
764 
765         if (wasConnectingInOrOut && !isConnectingInOrOut()) {
766             onConnectedInOrOut();
767         }
768 
769         if (changed && !wasHolding && (getState() == GsmCdmaCall.State.HOLDING)) {
770             // We've transitioned into HOLDING
771             onStartedHolding();
772         }
773 
774         return changed;
775     }
776 
777     /**
778      * Called when this Connection is in the foregroundCall
779      * when a dial is initiated.
780      * We know we're ACTIVE, and we know we're going to end up
781      * HOLDING in the backgroundCall
782      */
783     void
fakeHoldBeforeDial()784     fakeHoldBeforeDial() {
785         if (mParent != null) {
786             mParent.detach(this);
787         }
788 
789         mParent = mOwner.mBackgroundCall;
790         mParent.attachFake(this, GsmCdmaCall.State.HOLDING);
791 
792         onStartedHolding();
793     }
794 
795     /*package*/ int
getGsmCdmaIndex()796     getGsmCdmaIndex() throws CallStateException {
797         if (mIndex >= 0) {
798             return mIndex + 1;
799         } else {
800             throw new CallStateException ("GsmCdma index not yet assigned");
801         }
802     }
803 
804     /**
805      * An incoming or outgoing call has connected
806      */
807     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
808     void
onConnectedInOrOut()809     onConnectedInOrOut() {
810         mConnectTime = System.currentTimeMillis();
811         mConnectTimeReal = SystemClock.elapsedRealtime();
812         mDuration = 0;
813 
814         // bug #678474: incoming call interpreted as missed call, even though
815         // it sounds like the user has picked up the call.
816         if (Phone.DEBUG_PHONE) {
817             log("onConnectedInOrOut: connectTime=" + mConnectTime);
818         }
819 
820         if (!mIsIncoming) {
821             // outgoing calls only
822             processNextPostDialChar();
823         } else {
824             // Only release wake lock for incoming calls, for outgoing calls the wake lock
825             // will be released after any pause-dial is completed
826             releaseWakeLock();
827         }
828     }
829 
830     /**
831      * We have completed the migration of another connection to this GsmCdmaConnection (for example,
832      * in the case of SRVCC) and not still DIALING/ALERTING/INCOMING/WAITING.
833      */
onConnectedConnectionMigrated()834     void onConnectedConnectionMigrated() {
835         // We can release the wakelock in this case, the migrated call is not still
836         // DIALING/ALERTING/INCOMING/WAITING.
837         releaseWakeLock();
838     }
839 
840     private void
doDisconnect()841     doDisconnect() {
842         mIndex = -1;
843         mDisconnectTime = System.currentTimeMillis();
844         mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;
845         mDisconnected = true;
846         clearPostDialListeners();
847     }
848 
849     /*package*/ void
onStartedHolding()850     onStartedHolding() {
851         mHoldingStartTime = SystemClock.elapsedRealtime();
852     }
853 
854     /**
855      * Performs the appropriate action for a post-dial char, but does not
856      * notify application. returns false if the character is invalid and
857      * should be ignored
858      */
859     private boolean
processPostDialChar(char c)860     processPostDialChar(char c) {
861         if (PhoneNumberUtils.is12Key(c)) {
862             mOwner.mCi.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
863         } else if (isPause(c)) {
864             if (!isPhoneTypeGsm()) {
865                 setPostDialState(PostDialState.PAUSE);
866             }
867             // From TS 22.101:
868             // It continues...
869             // Upon the called party answering the UE shall send the DTMF digits
870             // automatically to the network after a delay of 3 seconds( 20 ).
871             // The digits shall be sent according to the procedures and timing
872             // specified in 3GPP TS 24.008 [13]. The first occurrence of the
873             // "DTMF Control Digits Separator" shall be used by the ME to
874             // distinguish between the addressing digits (i.e. the phone number)
875             // and the DTMF digits. Upon subsequent occurrences of the
876             // separator,
877             // the UE shall pause again for 3 seconds ( 20 ) before sending
878             // any further DTMF digits.
879             mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_PAUSE_DONE),
880                     isPhoneTypeGsm() ? PAUSE_DELAY_MILLIS_GSM: PAUSE_DELAY_MILLIS_CDMA);
881         } else if (isWait(c)) {
882             setPostDialState(PostDialState.WAIT);
883         } else if (isWild(c)) {
884             setPostDialState(PostDialState.WILD);
885         } else {
886             return false;
887         }
888 
889         return true;
890     }
891 
892     @Override
893     public String
getRemainingPostDialString()894     getRemainingPostDialString() {
895         String subStr = super.getRemainingPostDialString();
896         if (!isPhoneTypeGsm() && !TextUtils.isEmpty(subStr)) {
897             int wIndex = subStr.indexOf(PhoneNumberUtils.WAIT);
898             int pIndex = subStr.indexOf(PhoneNumberUtils.PAUSE);
899 
900             if (wIndex > 0 && (wIndex < pIndex || pIndex <= 0)) {
901                 subStr = subStr.substring(0, wIndex);
902             } else if (pIndex > 0) {
903                 subStr = subStr.substring(0, pIndex);
904             }
905         }
906         return subStr;
907     }
908 
909     //CDMA
910     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
updateParent(GsmCdmaCall oldParent, GsmCdmaCall newParent)911     public void updateParent(GsmCdmaCall oldParent, GsmCdmaCall newParent){
912         if (newParent != oldParent) {
913             if (oldParent != null) {
914                 oldParent.detach(this);
915             }
916             newParent.attachFake(this, GsmCdmaCall.State.ACTIVE);
917             mParent = newParent;
918         }
919     }
920 
921     @Override
finalize()922     protected void finalize()
923     {
924         /**
925          * It is understood that This finalizer is not guaranteed
926          * to be called and the release lock call is here just in
927          * case there is some path that doesn't call onDisconnect
928          * and or onConnectedInOrOut.
929          */
930         if (mPartialWakeLock != null && mPartialWakeLock.isHeld()) {
931             Rlog.e(LOG_TAG, "UNEXPECTED; mPartialWakeLock is held when finalizing.");
932         }
933         clearPostDialListeners();
934         releaseWakeLock();
935     }
936 
937     private void
processNextPostDialChar()938     processNextPostDialChar() {
939         char c = 0;
940         Registrant postDialHandler;
941 
942         if (mPostDialState == PostDialState.CANCELLED) {
943             releaseWakeLock();
944             return;
945         }
946 
947         if (mPostDialString == null ||
948                 mPostDialString.length() <= mNextPostDialChar) {
949             setPostDialState(PostDialState.COMPLETE);
950 
951             // We were holding a wake lock until pause-dial was complete, so give it up now
952             releaseWakeLock();
953 
954             // notifyMessage.arg1 is 0 on complete
955             c = 0;
956         } else {
957             boolean isValid;
958 
959             setPostDialState(PostDialState.STARTED);
960 
961             c = mPostDialString.charAt(mNextPostDialChar++);
962 
963             isValid = processPostDialChar(c);
964 
965             if (!isValid) {
966                 // Will call processNextPostDialChar
967                 mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget();
968                 // Don't notify application
969                 Rlog.e(LOG_TAG, "processNextPostDialChar: c=" + c + " isn't valid!");
970                 return;
971             }
972         }
973 
974         notifyPostDialListenersNextChar(c);
975 
976         // TODO: remove the following code since the handler no longer executes anything.
977         postDialHandler = mOwner.getPhone().getPostDialHandler();
978 
979         Message notifyMessage;
980 
981         if (postDialHandler != null
982                 && (notifyMessage = postDialHandler.messageForRegistrant()) != null) {
983             // The AsyncResult.result is the Connection object
984             PostDialState state = mPostDialState;
985             AsyncResult ar = AsyncResult.forMessage(notifyMessage);
986             ar.result = this;
987             ar.userObj = state;
988 
989             // arg1 is the character that was/is being processed
990             notifyMessage.arg1 = c;
991 
992             //Rlog.v("GsmCdma", "##### processNextPostDialChar: send msg to postDialHandler, arg1=" + c);
993             notifyMessage.sendToTarget();
994         }
995     }
996 
997     /** "connecting" means "has never been ACTIVE" for both incoming
998      *  and outgoing calls
999      */
1000     private boolean
isConnectingInOrOut()1001     isConnectingInOrOut() {
1002         return mParent == null || mParent == mOwner.mRingingCall
1003             || mParent.mState == GsmCdmaCall.State.DIALING
1004             || mParent.mState == GsmCdmaCall.State.ALERTING;
1005     }
1006 
1007     private GsmCdmaCall
parentFromDCState(DriverCall.State state)1008     parentFromDCState (DriverCall.State state) {
1009         switch (state) {
1010             case ACTIVE:
1011             case DIALING:
1012             case ALERTING:
1013                 return mOwner.mForegroundCall;
1014             //break;
1015 
1016             case HOLDING:
1017                 return mOwner.mBackgroundCall;
1018             //break;
1019 
1020             case INCOMING:
1021             case WAITING:
1022                 return mOwner.mRingingCall;
1023             //break;
1024 
1025             default:
1026                 throw new RuntimeException("illegal call state: " + state);
1027         }
1028     }
1029 
getAudioQualityFromDC(int audioQuality)1030     private int getAudioQualityFromDC(int audioQuality) {
1031         switch (audioQuality) {
1032             case DriverCall.AUDIO_QUALITY_AMR_WB:
1033             case DriverCall.AUDIO_QUALITY_EVRC_NW:
1034                 return Connection.AUDIO_QUALITY_HIGH_DEFINITION;
1035             default:
1036                 return Connection.AUDIO_QUALITY_STANDARD;
1037         }
1038     }
1039 
1040     /**
1041      * Set post dial state and acquire wake lock while switching to "started" or "pause"
1042      * state, the wake lock will be released if state switches out of "started" or "pause"
1043      * state or after WAKE_LOCK_TIMEOUT_MILLIS.
1044      * @param s new PostDialState
1045      */
setPostDialState(PostDialState s)1046     private void setPostDialState(PostDialState s) {
1047         if (s == PostDialState.STARTED
1048                 || s == PostDialState.PAUSE) {
1049             synchronized (mPartialWakeLock) {
1050                 if (mPartialWakeLock.isHeld()) {
1051                     mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
1052                 } else {
1053                     acquireWakeLock();
1054                 }
1055                 Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
1056                 mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS);
1057             }
1058         } else {
1059             mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
1060             releaseWakeLock();
1061         }
1062         mPostDialState = s;
1063         notifyPostDialListeners();
1064     }
1065 
1066     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
createWakeLock(Context context)1067     private void createWakeLock(Context context) {
1068         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
1069         mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
1070     }
1071 
1072     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
acquireWakeLock()1073     private void acquireWakeLock() {
1074         if (mPartialWakeLock != null) {
1075             synchronized (mPartialWakeLock) {
1076                 log("acquireWakeLock");
1077                 mPartialWakeLock.acquire();
1078             }
1079         }
1080     }
1081 
releaseWakeLock()1082     private void releaseWakeLock() {
1083         if (mPartialWakeLock != null) {
1084             synchronized (mPartialWakeLock) {
1085                 if (mPartialWakeLock.isHeld()) {
1086                     log("releaseWakeLock");
1087                     mPartialWakeLock.release();
1088                 }
1089             }
1090         }
1091     }
1092 
releaseAllWakeLocks()1093     private void releaseAllWakeLocks() {
1094         if (mPartialWakeLock != null) {
1095             synchronized (mPartialWakeLock) {
1096                 while (mPartialWakeLock.isHeld()) {
1097                     mPartialWakeLock.release();
1098                 }
1099             }
1100         }
1101     }
1102 
1103     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isPause(char c)1104     private static boolean isPause(char c) {
1105         return c == PhoneNumberUtils.PAUSE;
1106     }
1107 
1108     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isWait(char c)1109     private static boolean isWait(char c) {
1110         return c == PhoneNumberUtils.WAIT;
1111     }
1112 
isWild(char c)1113     private static boolean isWild(char c) {
1114         return c == PhoneNumberUtils.WILD;
1115     }
1116 
1117     //CDMA
1118     // This function is to find the next PAUSE character index if
1119     // multiple pauses in a row. Otherwise it finds the next non PAUSE or
1120     // non WAIT character index.
1121     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex)1122     private static int findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex) {
1123         boolean wMatched = isWait(phoneNumber.charAt(currIndex));
1124         int index = currIndex + 1;
1125         int length = phoneNumber.length();
1126         while (index < length) {
1127             char cNext = phoneNumber.charAt(index);
1128             // if there is any W inside P/W sequence,mark it
1129             if (isWait(cNext)) {
1130                 wMatched = true;
1131             }
1132             // if any characters other than P/W chars after P/W sequence
1133             // we break out the loop and append the correct
1134             if (!isWait(cNext) && !isPause(cNext)) {
1135                 break;
1136             }
1137             index++;
1138         }
1139 
1140         // It means the PAUSE character(s) is in the middle of dial string
1141         // and it needs to be handled one by one.
1142         if ((index < length) && (index > (currIndex + 1))  &&
1143                 ((wMatched == false) && isPause(phoneNumber.charAt(currIndex)))) {
1144             return (currIndex + 1);
1145         }
1146         return index;
1147     }
1148 
1149     // CDMA
1150     // This function returns either PAUSE or WAIT character to append.
1151     // It is based on the next non PAUSE/WAIT character in the phoneNumber and the
1152     // index for the current PAUSE/WAIT character
1153     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
findPOrWCharToAppend(String phoneNumber, int currPwIndex, int nextNonPwCharIndex)1154     private static char findPOrWCharToAppend(String phoneNumber, int currPwIndex,
1155                                              int nextNonPwCharIndex) {
1156         char c = phoneNumber.charAt(currPwIndex);
1157         char ret;
1158 
1159         // Append the PW char
1160         ret = (isPause(c)) ? PhoneNumberUtils.PAUSE : PhoneNumberUtils.WAIT;
1161 
1162         // If the nextNonPwCharIndex is greater than currPwIndex + 1,
1163         // it means the PW sequence contains not only P characters.
1164         // Since for the sequence that only contains P character,
1165         // the P character is handled one by one, the nextNonPwCharIndex
1166         // equals to currPwIndex + 1.
1167         // In this case, skip P, append W.
1168         if (nextNonPwCharIndex > (currPwIndex + 1)) {
1169             ret = PhoneNumberUtils.WAIT;
1170         }
1171         return ret;
1172     }
1173 
1174     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
maskDialString(String dialString)1175     private String maskDialString(String dialString) {
1176         if (VDBG) {
1177             return dialString;
1178         }
1179 
1180         return "<MASKED>";
1181     }
1182 
1183     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
fetchDtmfToneDelay(GsmCdmaPhone phone)1184     private void fetchDtmfToneDelay(GsmCdmaPhone phone) {
1185         CarrierConfigManager configMgr = (CarrierConfigManager)
1186                 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
1187         PersistableBundle b = configMgr.getConfigForSubId(phone.getSubId());
1188         if (b != null) {
1189             mDtmfToneDelay = b.getInt(phone.getDtmfToneDelayKey());
1190         }
1191     }
1192 
1193     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isPhoneTypeGsm()1194     private boolean isPhoneTypeGsm() {
1195         return mOwner.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_GSM;
1196     }
1197 
1198     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
log(String msg)1199     private void log(String msg) {
1200         Rlog.d(LOG_TAG, "[GsmCdmaConn] " + msg);
1201     }
1202 
1203     @Override
getNumberPresentation()1204     public int getNumberPresentation() {
1205         return mNumberPresentation;
1206     }
1207 
1208     @Override
getUUSInfo()1209     public UUSInfo getUUSInfo() {
1210         return mUusInfo;
1211     }
1212 
getPreciseDisconnectCause()1213     public int getPreciseDisconnectCause() {
1214         return mPreciseCause;
1215     }
1216 
1217     @Override
getVendorDisconnectCause()1218     public String getVendorDisconnectCause() {
1219         return mVendorCause;
1220     }
1221 
1222     @Override
migrateFrom(Connection c)1223     public void migrateFrom(Connection c) {
1224         if (c == null) return;
1225 
1226         super.migrateFrom(c);
1227 
1228         this.mUusInfo = c.getUUSInfo();
1229 
1230         this.setUserData(c.getUserData());
1231     }
1232 
1233     @Override
getOrigConnection()1234     public Connection getOrigConnection() {
1235         return mOrigConnection;
1236     }
1237 
1238     @Override
isMultiparty()1239     public boolean isMultiparty() {
1240         if (mOrigConnection != null) {
1241             return mOrigConnection.isMultiparty();
1242         }
1243 
1244         return false;
1245     }
1246 
1247     /**
1248      * Get the corresponding EmergencyNumberTracker associated with the connection.
1249      * @return the EmergencyNumberTracker
1250      */
getEmergencyNumberTracker()1251     public EmergencyNumberTracker getEmergencyNumberTracker() {
1252         if (mOwner != null) {
1253             Phone phone = mOwner.getPhone();
1254             if (phone != null) {
1255                 return phone.getEmergencyNumberTracker();
1256             }
1257         }
1258         return null;
1259     }
1260 
1261     /**
1262      * @return {@code true} if this call is an OTASP activation call, {@code false} otherwise.
1263      */
isOtaspCall()1264     public boolean isOtaspCall() {
1265         return mAddress != null && OTASP_NUMBER.equals(mAddress);
1266     }
1267 }
1268