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