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