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