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