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