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