CdmaConnection.java revision cbaa45bbf2cab852b6c9c3a887e9f803d4e857ea
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.telephony.Rlog; 29import android.text.TextUtils; 30 31import android.telephony.PhoneNumberUtils; 32import android.telephony.ServiceState; 33 34import com.android.internal.telephony.uicc.UiccCardApplication; 35import com.android.internal.telephony.uicc.UiccController; 36import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState; 37 38/** 39 * {@hide} 40 */ 41public 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 owner; 48 CdmaCall parent; 49 50 51 String address; // MAY BE NULL!!! 52 String dialString; // outgoing calls only 53 String postDialString; // outgoing calls only 54 boolean isIncoming; 55 boolean disconnected; 56 int index; // 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 createTime; 63 long connectTime; 64 long disconnectTime; 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 connectTimeReal; 72 long duration; 73 long holdingStartTime; // The time when the Connection last transitioned 74 // into HOLDING 75 76 int nextPostDialChar; // index into postDialString 77 78 DisconnectCause cause = DisconnectCause.NOT_DISCONNECTED; 79 PostDialState postDialState = PostDialState.NOT_STARTED; 80 int numberPresentation = PhoneConstants.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 @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 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 Rlog.d(LOG_TAG, "[CDMAConn] CdmaConnection: dialString=" + dialString); 155 dialString = formatDialString(dialString); 156 Rlog.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 @Override 227 public String getOrigDialString(){ 228 return dialString; 229 } 230 231 @Override 232 public String getAddress() { 233 return address; 234 } 235 236 @Override 237 public CdmaCall getCall() { 238 return parent; 239 } 240 241 @Override 242 public long getCreateTime() { 243 return createTime; 244 } 245 246 @Override 247 public long getConnectTime() { 248 return connectTime; 249 } 250 251 @Override 252 public long getDisconnectTime() { 253 return disconnectTime; 254 } 255 256 @Override 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 @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() - holdingStartTime; 274 } 275 } 276 277 @Override 278 public DisconnectCause getDisconnectCause() { 279 return cause; 280 } 281 282 @Override 283 public boolean isIncoming() { 284 return isIncoming; 285 } 286 287 @Override 288 public CdmaCall.State getState() { 289 if (disconnected) { 290 return CdmaCall.State.DISCONNECTED; 291 } else { 292 return super.getState(); 293 } 294 } 295 296 @Override 297 public void hangup() throws CallStateException { 298 if (!disconnected) { 299 owner.hangup(this); 300 } else { 301 throw new CallStateException ("disconnected"); 302 } 303 } 304 305 @Override 306 public void separate() throws CallStateException { 307 if (!disconnected) { 308 owner.separate(this); 309 } else { 310 throw new CallStateException ("disconnected"); 311 } 312 } 313 314 @Override 315 public PostDialState getPostDialState() { 316 return postDialState; 317 } 318 319 @Override 320 public void proceedAfterWaitChar() { 321 if (postDialState != PostDialState.WAIT) { 322 Rlog.w(LOG_TAG, "CdmaConnection.proceedAfterWaitChar(): Expected " 323 + "getPostDialState() to be WAIT but was " + postDialState); 324 return; 325 } 326 327 setPostDialState(PostDialState.STARTED); 328 329 processNextPostDialChar(); 330 } 331 332 @Override 333 public void proceedAfterWildChar(String str) { 334 if (postDialState != PostDialState.WILD) { 335 Rlog.w(LOG_TAG, "CdmaConnection.proceedAfterWaitChar(): Expected " 336 + "getPostDialState() to be WILD but was " + postDialState); 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(postDialString.substring(nextPostDialChar)); 347 postDialString = buf.toString(); 348 nextPostDialChar = 0; 349 if (Phone.DEBUG_PHONE) { 350 log("proceedAfterWildChar: new postDialString is " + 351 postDialString); 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 cause = 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 = owner.phone; 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 this.cause = cause; 445 446 if (!disconnected) { 447 doDisconnect(); 448 if (VDBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause); 449 450 owner.phone.notifyDisconnect(this); 451 452 if (parent != null) { 453 parent.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 (!disconnected) { 463 doDisconnect(); 464 if (VDBG) Rlog.d(LOG_TAG, "onLoalDisconnect" ); 465 466 if (parent != null) { 467 parent.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= " +parent +", newParent= " + newParent); 484 485 if (!equalsHandlesNulls(address, dc.number)) { 486 if (Phone.DEBUG_PHONE) log("update: phone # changed!"); 487 address = 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(cnapName)) { 494 changed = true; 495 cnapName = ""; 496 } 497 } else if (!dc.name.equals(cnapName)) { 498 changed = true; 499 cnapName = dc.name; 500 } 501 502 if (Phone.DEBUG_PHONE) log("--dssds----"+cnapName); 503 cnapNamePresentation = dc.namePresentation; 504 numberPresentation = dc.numberPresentation; 505 506 if (newParent != parent) { 507 if (parent != null) { 508 parent.detach(this); 509 } 510 newParent.attach(this, dc); 511 parent = newParent; 512 changed = true; 513 } else { 514 boolean parentStateChange; 515 parentStateChange = parent.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 (parent != null) { 549 parent.detach(this); 550 } 551 552 parent = owner.backgroundCall; 553 parent.attachFake(this, CdmaCall.State.HOLDING); 554 555 onStartedHolding(); 556 } 557 558 /*package*/ int 559 getCDMAIndex() throws CallStateException { 560 if (index >= 0) { 561 return index + 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 connectTime = System.currentTimeMillis(); 573 connectTimeReal = SystemClock.elapsedRealtime(); 574 duration = 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=" + connectTime); 580 } 581 582 if (!isIncoming) { 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 index = -1; 595 disconnectTime = System.currentTimeMillis(); 596 duration = SystemClock.elapsedRealtime() - connectTimeReal; 597 disconnected = true; 598 } 599 600 /*package*/ void 601 onStartedHolding() { 602 holdingStartTime = 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 owner.cm.sendDtmf(c, h.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 h.sendMessageDelayed(h.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 (postDialState == PostDialState.CANCELLED 635 || postDialState == PostDialState.COMPLETE 636 || postDialString == null 637 || postDialString.length() <= nextPostDialChar) { 638 return ""; 639 } 640 641 String subStr = postDialString.substring(nextPostDialChar); 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 parent = 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 (postDialState == PostDialState.CANCELLED) { 685 releaseWakeLock(); 686 //Rlog.v("CDMA", "##### processNextPostDialChar: postDialState == CANCELLED, bail"); 687 return; 688 } 689 690 if (postDialString == null || 691 postDialString.length() <= nextPostDialChar) { 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 = postDialString.charAt(nextPostDialChar++); 705 706 isValid = processPostDialChar(c); 707 708 if (!isValid) { 709 // Will call processNextPostDialChar 710 h.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 = owner.phone.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 = postDialState; 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 parent == null || parent == owner.ringingCall 743 || parent.state == CdmaCall.State.DIALING 744 || parent.state == 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 owner.foregroundCall; 754 //break; 755 756 case HOLDING: 757 return owner.backgroundCall; 758 //break; 759 760 case INCOMING: 761 case WAITING: 762 return owner.ringingCall; 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 h.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); 782 } else { 783 acquireWakeLock(); 784 } 785 Message msg = h.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT); 786 h.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS); 787 } 788 } else { 789 h.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); 790 releaseWakeLock(); 791 } 792 postDialState = 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 numberPresentation; 938 } 939 940 @Override 941 public UUSInfo getUUSInfo() { 942 // UUS information not supported in CDMA 943 return null; 944 } 945} 946