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*/ boolean 443 onDisconnect(DisconnectCause cause) { 444 boolean changed = false; 445 446 mCause = cause; 447 448 if (!mDisconnected) { 449 doDisconnect(); 450 if (VDBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause); 451 452 mOwner.mPhone.notifyDisconnect(this); 453 454 if (mParent != null) { 455 changed = mParent.connectionDisconnected(this); 456 } 457 } 458 releaseWakeLock(); 459 return changed; 460 } 461 462 /** Called when the call waiting connection has been hung up */ 463 /*package*/ void 464 onLocalDisconnect() { 465 if (!mDisconnected) { 466 doDisconnect(); 467 if (VDBG) Rlog.d(LOG_TAG, "onLoalDisconnect" ); 468 469 if (mParent != null) { 470 mParent.detach(this); 471 } 472 } 473 releaseWakeLock(); 474 } 475 476 // Returns true if state has changed, false if nothing changed 477 /*package*/ boolean 478 update (DriverCall dc) { 479 CdmaCall newParent; 480 boolean changed = false; 481 boolean wasConnectingInOrOut = isConnectingInOrOut(); 482 boolean wasHolding = (getState() == CdmaCall.State.HOLDING); 483 484 newParent = parentFromDCState(dc.state); 485 486 if (Phone.DEBUG_PHONE) log("parent= " +mParent +", newParent= " + newParent); 487 488 if (!equalsHandlesNulls(mAddress, dc.number)) { 489 if (Phone.DEBUG_PHONE) log("update: phone # changed!"); 490 mAddress = dc.number; 491 changed = true; 492 } 493 494 // A null cnapName should be the same as "" 495 if (TextUtils.isEmpty(dc.name)) { 496 if (!TextUtils.isEmpty(mCnapName)) { 497 changed = true; 498 mCnapName = ""; 499 } 500 } else if (!dc.name.equals(mCnapName)) { 501 changed = true; 502 mCnapName = dc.name; 503 } 504 505 if (Phone.DEBUG_PHONE) log("--dssds----"+mCnapName); 506 mCnapNamePresentation = dc.namePresentation; 507 mNumberPresentation = dc.numberPresentation; 508 509 if (newParent != mParent) { 510 if (mParent != null) { 511 mParent.detach(this); 512 } 513 newParent.attach(this, dc); 514 mParent = newParent; 515 changed = true; 516 } else { 517 boolean parentStateChange; 518 parentStateChange = mParent.update (this, dc); 519 changed = changed || parentStateChange; 520 } 521 522 /** Some state-transition events */ 523 524 if (Phone.DEBUG_PHONE) log( 525 "Update, wasConnectingInOrOut=" + wasConnectingInOrOut + 526 ", wasHolding=" + wasHolding + 527 ", isConnectingInOrOut=" + isConnectingInOrOut() + 528 ", changed=" + changed); 529 530 531 if (wasConnectingInOrOut && !isConnectingInOrOut()) { 532 onConnectedInOrOut(); 533 } 534 535 if (changed && !wasHolding && (getState() == CdmaCall.State.HOLDING)) { 536 // We've transitioned into HOLDING 537 onStartedHolding(); 538 } 539 540 return changed; 541 } 542 543 /** 544 * Called when this Connection is in the foregroundCall 545 * when a dial is initiated. 546 * We know we're ACTIVE, and we know we're going to end up 547 * HOLDING in the backgroundCall 548 */ 549 void 550 fakeHoldBeforeDial() { 551 if (mParent != null) { 552 mParent.detach(this); 553 } 554 555 mParent = mOwner.mBackgroundCall; 556 mParent.attachFake(this, CdmaCall.State.HOLDING); 557 558 onStartedHolding(); 559 } 560 561 /*package*/ int 562 getCDMAIndex() throws CallStateException { 563 if (mIndex >= 0) { 564 return mIndex + 1; 565 } else { 566 throw new CallStateException ("CDMA connection index not assigned"); 567 } 568 } 569 570 /** 571 * An incoming or outgoing call has connected 572 */ 573 void 574 onConnectedInOrOut() { 575 mConnectTime = System.currentTimeMillis(); 576 mConnectTimeReal = SystemClock.elapsedRealtime(); 577 mDuration = 0; 578 579 // bug #678474: incoming call interpreted as missed call, even though 580 // it sounds like the user has picked up the call. 581 if (Phone.DEBUG_PHONE) { 582 log("onConnectedInOrOut: connectTime=" + mConnectTime); 583 } 584 585 if (!mIsIncoming) { 586 // outgoing calls only 587 processNextPostDialChar(); 588 } else { 589 // Only release wake lock for incoming calls, for outgoing calls the wake lock 590 // will be released after any pause-dial is completed 591 releaseWakeLock(); 592 } 593 } 594 595 private void 596 doDisconnect() { 597 mIndex = -1; 598 mDisconnectTime = System.currentTimeMillis(); 599 mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal; 600 mDisconnected = true; 601 } 602 603 /*package*/ void 604 onStartedHolding() { 605 mHoldingStartTime = SystemClock.elapsedRealtime(); 606 } 607 /** 608 * Performs the appropriate action for a post-dial char, but does not 609 * notify application. returns false if the character is invalid and 610 * should be ignored 611 */ 612 private boolean 613 processPostDialChar(char c) { 614 if (PhoneNumberUtils.is12Key(c)) { 615 mOwner.mCi.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE)); 616 } else if (c == PhoneNumberUtils.PAUSE) { 617 setPostDialState(PostDialState.PAUSE); 618 619 // Upon occurrences of the separator, the UE shall 620 // pause again for 2 seconds before sending any 621 // further DTMF digits. 622 mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_PAUSE_DONE), 623 PAUSE_DELAY_MILLIS); 624 } else if (c == PhoneNumberUtils.WAIT) { 625 setPostDialState(PostDialState.WAIT); 626 } else if (c == PhoneNumberUtils.WILD) { 627 setPostDialState(PostDialState.WILD); 628 } else { 629 return false; 630 } 631 632 return true; 633 } 634 635 @Override 636 public String getRemainingPostDialString() { 637 if (mPostDialState == PostDialState.CANCELLED 638 || mPostDialState == PostDialState.COMPLETE 639 || mPostDialString == null 640 || mPostDialString.length() <= mNextPostDialChar) { 641 return ""; 642 } 643 644 String subStr = mPostDialString.substring(mNextPostDialChar); 645 if (subStr != null) { 646 int wIndex = subStr.indexOf(PhoneNumberUtils.WAIT); 647 int pIndex = subStr.indexOf(PhoneNumberUtils.PAUSE); 648 649 if (wIndex > 0 && (wIndex < pIndex || pIndex <= 0)) { 650 subStr = subStr.substring(0, wIndex); 651 } else if (pIndex > 0) { 652 subStr = subStr.substring(0, pIndex); 653 } 654 } 655 return subStr; 656 } 657 658 public void updateParent(CdmaCall oldParent, CdmaCall newParent){ 659 if (newParent != oldParent) { 660 if (oldParent != null) { 661 oldParent.detach(this); 662 } 663 newParent.attachFake(this, CdmaCall.State.ACTIVE); 664 mParent = newParent; 665 } 666 } 667 668 @Override 669 protected void finalize() 670 { 671 /** 672 * It is understood that This finializer is not guaranteed 673 * to be called and the release lock call is here just in 674 * case there is some path that doesn't call onDisconnect 675 * and or onConnectedInOrOut. 676 */ 677 if (mPartialWakeLock.isHeld()) { 678 Rlog.e(LOG_TAG, "[CdmaConn] UNEXPECTED; mPartialWakeLock is held when finalizing."); 679 } 680 releaseWakeLock(); 681 } 682 683 void processNextPostDialChar() { 684 char c = 0; 685 Registrant postDialHandler; 686 687 if (mPostDialState == PostDialState.CANCELLED) { 688 releaseWakeLock(); 689 //Rlog.v("CDMA", "##### processNextPostDialChar: postDialState == CANCELLED, bail"); 690 return; 691 } 692 693 if (mPostDialString == null || 694 mPostDialString.length() <= mNextPostDialChar) { 695 setPostDialState(PostDialState.COMPLETE); 696 697 // We were holding a wake lock until pause-dial was complete, so give it up now 698 releaseWakeLock(); 699 700 // notifyMessage.arg1 is 0 on complete 701 c = 0; 702 } else { 703 boolean isValid; 704 705 setPostDialState(PostDialState.STARTED); 706 707 c = mPostDialString.charAt(mNextPostDialChar++); 708 709 isValid = processPostDialChar(c); 710 711 if (!isValid) { 712 // Will call processNextPostDialChar 713 mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget(); 714 // Don't notify application 715 Rlog.e("CDMA", "processNextPostDialChar: c=" + c + " isn't valid!"); 716 return; 717 } 718 } 719 720 postDialHandler = mOwner.mPhone.mPostDialHandler; 721 722 Message notifyMessage; 723 724 if (postDialHandler != null && 725 (notifyMessage = postDialHandler.messageForRegistrant()) != null) { 726 // The AsyncResult.result is the Connection object 727 PostDialState state = mPostDialState; 728 AsyncResult ar = AsyncResult.forMessage(notifyMessage); 729 ar.result = this; 730 ar.userObj = state; 731 732 // arg1 is the character that was/is being processed 733 notifyMessage.arg1 = c; 734 735 notifyMessage.sendToTarget(); 736 } 737 } 738 739 740 /** "connecting" means "has never been ACTIVE" for both incoming 741 * and outgoing calls 742 */ 743 private boolean 744 isConnectingInOrOut() { 745 return mParent == null || mParent == mOwner.mRingingCall 746 || mParent.mState == CdmaCall.State.DIALING 747 || mParent.mState == CdmaCall.State.ALERTING; 748 } 749 750 private CdmaCall 751 parentFromDCState (DriverCall.State state) { 752 switch (state) { 753 case ACTIVE: 754 case DIALING: 755 case ALERTING: 756 return mOwner.mForegroundCall; 757 //break; 758 759 case HOLDING: 760 return mOwner.mBackgroundCall; 761 //break; 762 763 case INCOMING: 764 case WAITING: 765 return mOwner.mRingingCall; 766 //break; 767 768 default: 769 throw new RuntimeException("illegal call state: " + state); 770 } 771 } 772 773 /** 774 * Set post dial state and acquire wake lock while switching to "started" or "wait" 775 * state, the wake lock will be released if state switches out of "started" or "wait" 776 * state or after WAKE_LOCK_TIMEOUT_MILLIS. 777 * @param s new PostDialState 778 */ 779 private void setPostDialState(PostDialState s) { 780 if (s == PostDialState.STARTED || 781 s == PostDialState.PAUSE) { 782 synchronized (mPartialWakeLock) { 783 if (mPartialWakeLock.isHeld()) { 784 mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); 785 } else { 786 acquireWakeLock(); 787 } 788 Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT); 789 mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS); 790 } 791 } else { 792 mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); 793 releaseWakeLock(); 794 } 795 mPostDialState = s; 796 } 797 798 private void createWakeLock(Context context) { 799 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 800 mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); 801 } 802 803 private void acquireWakeLock() { 804 log("acquireWakeLock"); 805 mPartialWakeLock.acquire(); 806 } 807 808 private void releaseWakeLock() { 809 synchronized (mPartialWakeLock) { 810 if (mPartialWakeLock.isHeld()) { 811 log("releaseWakeLock"); 812 mPartialWakeLock.release(); 813 } 814 } 815 } 816 817 private static boolean isPause(char c) { 818 return c == PhoneNumberUtils.PAUSE; 819 } 820 821 private static boolean isWait(char c) { 822 return c == PhoneNumberUtils.WAIT; 823 } 824 825 // This function is to find the next PAUSE character index if 826 // multiple pauses in a row. Otherwise it finds the next non PAUSE or 827 // non WAIT character index. 828 private static int 829 findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex) { 830 boolean wMatched = isWait(phoneNumber.charAt(currIndex)); 831 int index = currIndex + 1; 832 int length = phoneNumber.length(); 833 while (index < length) { 834 char cNext = phoneNumber.charAt(index); 835 // if there is any W inside P/W sequence,mark it 836 if (isWait(cNext)) { 837 wMatched = true; 838 } 839 // if any characters other than P/W chars after P/W sequence 840 // we break out the loop and append the correct 841 if (!isWait(cNext) && !isPause(cNext)) { 842 break; 843 } 844 index++; 845 } 846 847 // It means the PAUSE character(s) is in the middle of dial string 848 // and it needs to be handled one by one. 849 if ((index < length) && (index > (currIndex + 1)) && 850 ((wMatched == false) && isPause(phoneNumber.charAt(currIndex)))) { 851 return (currIndex + 1); 852 } 853 return index; 854 } 855 856 // This function returns either PAUSE or WAIT character to append. 857 // It is based on the next non PAUSE/WAIT character in the phoneNumber and the 858 // index for the current PAUSE/WAIT character 859 private static char 860 findPOrWCharToAppend(String phoneNumber, int currPwIndex, int nextNonPwCharIndex) { 861 char c = phoneNumber.charAt(currPwIndex); 862 char ret; 863 864 // Append the PW char 865 ret = (isPause(c)) ? PhoneNumberUtils.PAUSE : PhoneNumberUtils.WAIT; 866 867 // If the nextNonPwCharIndex is greater than currPwIndex + 1, 868 // it means the PW sequence contains not only P characters. 869 // Since for the sequence that only contains P character, 870 // the P character is handled one by one, the nextNonPwCharIndex 871 // equals to currPwIndex + 1. 872 // In this case, skip P, append W. 873 if (nextNonPwCharIndex > (currPwIndex + 1)) { 874 ret = PhoneNumberUtils.WAIT; 875 } 876 return ret; 877 } 878 879 /** 880 * format original dial string 881 * 1) convert international dialing prefix "+" to 882 * string specified per region 883 * 884 * 2) handle corner cases for PAUSE/WAIT dialing: 885 * 886 * If PAUSE/WAIT sequence at the end, ignore them. 887 * 888 * If consecutive PAUSE/WAIT sequence in the middle of the string, 889 * and if there is any WAIT in PAUSE/WAIT sequence, treat them like WAIT. 890 */ 891 public static String formatDialString(String phoneNumber) { 892 /** 893 * TODO(cleanup): This function should move to PhoneNumberUtils, and 894 * tests should be added. 895 */ 896 897 if (phoneNumber == null) { 898 return null; 899 } 900 int length = phoneNumber.length(); 901 StringBuilder ret = new StringBuilder(); 902 char c; 903 int currIndex = 0; 904 905 while (currIndex < length) { 906 c = phoneNumber.charAt(currIndex); 907 if (isPause(c) || isWait(c)) { 908 if (currIndex < length - 1) { 909 // if PW not at the end 910 int nextIndex = findNextPCharOrNonPOrNonWCharIndex(phoneNumber, currIndex); 911 // If there is non PW char following PW sequence 912 if (nextIndex < length) { 913 char pC = findPOrWCharToAppend(phoneNumber, currIndex, nextIndex); 914 ret.append(pC); 915 // If PW char sequence has more than 2 PW characters, 916 // skip to the last PW character since the sequence already be 917 // converted to WAIT character 918 if (nextIndex > (currIndex + 1)) { 919 currIndex = nextIndex - 1; 920 } 921 } else if (nextIndex == length) { 922 // It means PW characters at the end, ignore 923 currIndex = length - 1; 924 } 925 } 926 } else { 927 ret.append(c); 928 } 929 currIndex++; 930 } 931 return PhoneNumberUtils.cdmaCheckAndProcessPlusCode(ret.toString()); 932 } 933 934 private void log(String msg) { 935 Rlog.d(LOG_TAG, "[CDMAConn] " + msg); 936 } 937 938 @Override 939 public int getNumberPresentation() { 940 return mNumberPresentation; 941 } 942 943 @Override 944 public UUSInfo getUUSInfo() { 945 // UUS information not supported in CDMA 946 return null; 947 } 948 } 949