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