CdmaConnection.java revision c38bb60d867c5d61d90b7179a9ed2b2d1848124f
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; 34import com.android.internal.telephony.TelephonyProperties; 35import com.android.internal.telephony.RILConstants; 36 37/** 38 * {@hide} 39 */ 40public 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 int index; // index in CdmaCallTracker.connections[], -1 if unassigned 55 56 /* 57 * These time/timespan values are based on System.currentTimeMillis(), 58 * i.e., "wall clock" time. 59 */ 60 long createTime; 61 long connectTime; 62 long disconnectTime; 63 64 /* 65 * These time/timespan values are based on SystemClock.elapsedRealTime(), 66 * i.e., time since boot. They are appropriate for comparison and 67 * calculating deltas. 68 */ 69 long connectTimeReal; 70 long duration; 71 long holdingStartTime; // The time when the Connection last transitioned 72 // into HOLDING 73 74 int nextPostDialChar; // index into postDialString 75 76 DisconnectCause cause = DisconnectCause.NOT_DISCONNECTED; 77 PostDialState postDialState = PostDialState.NOT_STARTED; 78 int numberPresentation = PhoneConstants.PRESENTATION_ALLOWED; 79 80 81 Handler h; 82 83 private PowerManager.WakeLock mPartialWakeLock; 84 85 //***** Event Constants 86 static final int EVENT_DTMF_DONE = 1; 87 static final int EVENT_PAUSE_DONE = 2; 88 static final int EVENT_NEXT_POST_DIAL = 3; 89 static final int EVENT_WAKE_LOCK_TIMEOUT = 4; 90 91 //***** Constants 92 static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000; 93 static final int PAUSE_DELAY_MILLIS = 2 * 1000; 94 95 //***** Inner Classes 96 97 class MyHandler extends Handler { 98 MyHandler(Looper l) {super(l);} 99 100 public void 101 handleMessage(Message msg) { 102 103 switch (msg.what) { 104 case EVENT_NEXT_POST_DIAL: 105 case EVENT_DTMF_DONE: 106 case EVENT_PAUSE_DONE: 107 processNextPostDialChar(); 108 break; 109 case EVENT_WAKE_LOCK_TIMEOUT: 110 releaseWakeLock(); 111 break; 112 } 113 } 114 } 115 116 //***** Constructors 117 118 /** This is probably an MT call that we first saw in a CLCC response */ 119 /*package*/ 120 CdmaConnection (Context context, DriverCall dc, CdmaCallTracker ct, int index) { 121 createWakeLock(context); 122 acquireWakeLock(); 123 124 owner = ct; 125 h = new MyHandler(owner.getLooper()); 126 127 address = dc.number; 128 129 isIncoming = dc.isMT; 130 createTime = System.currentTimeMillis(); 131 cnapName = dc.name; 132 cnapNamePresentation = dc.namePresentation; 133 numberPresentation = dc.numberPresentation; 134 135 this.index = index; 136 137 parent = parentFromDCState (dc.state); 138 parent.attach(this, dc); 139 } 140 141 /** This is an MO call/three way call, created when dialing */ 142 /*package*/ 143 CdmaConnection(Context context, String dialString, CdmaCallTracker ct, CdmaCall parent) { 144 createWakeLock(context); 145 acquireWakeLock(); 146 147 owner = ct; 148 h = new MyHandler(owner.getLooper()); 149 150 this.dialString = dialString; 151 Log.d(LOG_TAG, "[CDMAConn] CdmaConnection: dialString=" + dialString); 152 dialString = formatDialString(dialString); 153 Log.d(LOG_TAG, "[CDMAConn] CdmaConnection:formated dialString=" + dialString); 154 155 this.address = PhoneNumberUtils.extractNetworkPortionAlt(dialString); 156 this.postDialString = PhoneNumberUtils.extractPostDialPortion(dialString); 157 158 index = -1; 159 160 isIncoming = false; 161 cnapName = null; 162 cnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED; 163 numberPresentation = PhoneConstants.PRESENTATION_ALLOWED; 164 createTime = System.currentTimeMillis(); 165 166 if (parent != null) { 167 this.parent = parent; 168 169 //for the three way call case, not change parent state 170 if (parent.state == CdmaCall.State.ACTIVE) { 171 parent.attachFake(this, CdmaCall.State.ACTIVE); 172 } else { 173 parent.attachFake(this, CdmaCall.State.DIALING); 174 } 175 } 176 } 177 178 /** This is a Call waiting call*/ 179 CdmaConnection(Context context, CdmaCallWaitingNotification cw, CdmaCallTracker ct, 180 CdmaCall parent) { 181 createWakeLock(context); 182 acquireWakeLock(); 183 184 owner = ct; 185 h = new MyHandler(owner.getLooper()); 186 address = cw.number; 187 numberPresentation = cw.numberPresentation; 188 cnapName = cw.name; 189 cnapNamePresentation = cw.namePresentation; 190 index = -1; 191 isIncoming = true; 192 createTime = System.currentTimeMillis(); 193 connectTime = 0; 194 this.parent = parent; 195 parent.attachFake(this, CdmaCall.State.WAITING); 196 } 197 198 public void dispose() { 199 } 200 201 static boolean 202 equalsHandlesNulls (Object a, Object b) { 203 return (a == null) ? (b == null) : a.equals (b); 204 } 205 206 /*package*/ boolean 207 compareTo(DriverCall c) { 208 // On mobile originated (MO) calls, the phone number may have changed 209 // due to a SIM Toolkit call control modification. 210 // 211 // We assume we know when MO calls are created (since we created them) 212 // and therefore don't need to compare the phone number anyway. 213 if (! (isIncoming || c.isMT)) return true; 214 215 // ... but we can compare phone numbers on MT calls, and we have 216 // no control over when they begin, so we might as well 217 218 String cAddress = PhoneNumberUtils.stringFromStringAndTOA(c.number, c.TOA); 219 return isIncoming == c.isMT && equalsHandlesNulls(address, cAddress); 220 } 221 222 223 public String getOrigDialString(){ 224 return dialString; 225 } 226 227 public String getAddress() { 228 return address; 229 } 230 231 public CdmaCall getCall() { 232 return parent; 233 } 234 235 public long getCreateTime() { 236 return createTime; 237 } 238 239 public long getConnectTime() { 240 return connectTime; 241 } 242 243 public long getDisconnectTime() { 244 return disconnectTime; 245 } 246 247 public long getDurationMillis() { 248 if (connectTimeReal == 0) { 249 return 0; 250 } else if (duration == 0) { 251 return SystemClock.elapsedRealtime() - connectTimeReal; 252 } else { 253 return duration; 254 } 255 } 256 257 public long getHoldDurationMillis() { 258 if (getState() != CdmaCall.State.HOLDING) { 259 // If not holding, return 0 260 return 0; 261 } else { 262 return SystemClock.elapsedRealtime() - holdingStartTime; 263 } 264 } 265 266 public DisconnectCause getDisconnectCause() { 267 return cause; 268 } 269 270 public boolean isIncoming() { 271 return isIncoming; 272 } 273 274 public CdmaCall.State getState() { 275 if (disconnected) { 276 return CdmaCall.State.DISCONNECTED; 277 } else { 278 return super.getState(); 279 } 280 } 281 282 public void hangup() throws CallStateException { 283 if (!disconnected) { 284 owner.hangup(this); 285 } else { 286 throw new CallStateException ("disconnected"); 287 } 288 } 289 290 public void separate() throws CallStateException { 291 if (!disconnected) { 292 owner.separate(this); 293 } else { 294 throw new CallStateException ("disconnected"); 295 } 296 } 297 298 public PostDialState getPostDialState() { 299 return postDialState; 300 } 301 302 public void proceedAfterWaitChar() { 303 if (postDialState != PostDialState.WAIT) { 304 Log.w(LOG_TAG, "CdmaConnection.proceedAfterWaitChar(): Expected " 305 + "getPostDialState() to be WAIT but was " + postDialState); 306 return; 307 } 308 309 setPostDialState(PostDialState.STARTED); 310 311 processNextPostDialChar(); 312 } 313 314 public void proceedAfterWildChar(String str) { 315 if (postDialState != PostDialState.WILD) { 316 Log.w(LOG_TAG, "CdmaConnection.proceedAfterWaitChar(): Expected " 317 + "getPostDialState() to be WILD but was " + postDialState); 318 return; 319 } 320 321 setPostDialState(PostDialState.STARTED); 322 323 if (false) { 324 boolean playedTone = false; 325 int len = (str != null ? str.length() : 0); 326 327 for (int i=0; i<len; i++) { 328 char c = str.charAt(i); 329 Message msg = null; 330 331 if (i == len-1) { 332 msg = h.obtainMessage(EVENT_DTMF_DONE); 333 } 334 335 if (PhoneNumberUtils.is12Key(c)) { 336 owner.cm.sendDtmf(c, msg); 337 playedTone = true; 338 } 339 } 340 341 if (!playedTone) { 342 processNextPostDialChar(); 343 } 344 } else { 345 // make a new postDialString, with the wild char replacement string 346 // at the beginning, followed by the remaining postDialString. 347 348 StringBuilder buf = new StringBuilder(str); 349 buf.append(postDialString.substring(nextPostDialChar)); 350 postDialString = buf.toString(); 351 nextPostDialChar = 0; 352 if (Phone.DEBUG_PHONE) { 353 log("proceedAfterWildChar: new postDialString is " + 354 postDialString); 355 } 356 357 processNextPostDialChar(); 358 } 359 } 360 361 public void cancelPostDial() { 362 setPostDialState(PostDialState.CANCELLED); 363 } 364 365 /** 366 * Called when this Connection is being hung up locally (eg, user pressed "end") 367 * Note that at this point, the hangup request has been dispatched to the radio 368 * but no response has yet been received so update() has not yet been called 369 */ 370 void 371 onHangupLocal() { 372 cause = DisconnectCause.LOCAL; 373 } 374 375 DisconnectCause 376 disconnectCauseFromCode(int causeCode) { 377 /** 378 * See 22.001 Annex F.4 for mapping of cause codes 379 * to local tones 380 */ 381 382 switch (causeCode) { 383 case CallFailCause.USER_BUSY: 384 return DisconnectCause.BUSY; 385 case CallFailCause.NO_CIRCUIT_AVAIL: 386 return DisconnectCause.CONGESTION; 387 case CallFailCause.ACM_LIMIT_EXCEEDED: 388 return DisconnectCause.LIMIT_EXCEEDED; 389 case CallFailCause.CALL_BARRED: 390 return DisconnectCause.CALL_BARRED; 391 case CallFailCause.FDN_BLOCKED: 392 return DisconnectCause.FDN_BLOCKED; 393 case CallFailCause.CDMA_LOCKED_UNTIL_POWER_CYCLE: 394 return DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE; 395 case CallFailCause.CDMA_DROP: 396 return DisconnectCause.CDMA_DROP; 397 case CallFailCause.CDMA_INTERCEPT: 398 return DisconnectCause.CDMA_INTERCEPT; 399 case CallFailCause.CDMA_REORDER: 400 return DisconnectCause.CDMA_REORDER; 401 case CallFailCause.CDMA_SO_REJECT: 402 return DisconnectCause.CDMA_SO_REJECT; 403 case CallFailCause.CDMA_RETRY_ORDER: 404 return DisconnectCause.CDMA_RETRY_ORDER; 405 case CallFailCause.CDMA_ACCESS_FAILURE: 406 return DisconnectCause.CDMA_ACCESS_FAILURE; 407 case CallFailCause.CDMA_PREEMPTED: 408 return DisconnectCause.CDMA_PREEMPTED; 409 case CallFailCause.CDMA_NOT_EMERGENCY: 410 return DisconnectCause.CDMA_NOT_EMERGENCY; 411 case CallFailCause.CDMA_ACCESS_BLOCKED: 412 return DisconnectCause.CDMA_ACCESS_BLOCKED; 413 case CallFailCause.ERROR_UNSPECIFIED: 414 case CallFailCause.NORMAL_CLEARING: 415 default: 416 CDMAPhone phone = owner.phone; 417 int serviceState = phone.getServiceState().getState(); 418 if (serviceState == ServiceState.STATE_POWER_OFF) { 419 return DisconnectCause.POWER_OFF; 420 } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE 421 || serviceState == ServiceState.STATE_EMERGENCY_ONLY) { 422 return DisconnectCause.OUT_OF_SERVICE; 423 } else if (phone.mCdmaSubscriptionSource == 424 CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM 425 && phone.getIccCard().getState() != IccCardConstants.State.READY) { 426 return DisconnectCause.ICC_ERROR; 427 } else if (causeCode==CallFailCause.NORMAL_CLEARING) { 428 return DisconnectCause.NORMAL; 429 } else { 430 return DisconnectCause.ERROR_UNSPECIFIED; 431 } 432 } 433 } 434 435 /*package*/ void 436 onRemoteDisconnect(int causeCode) { 437 onDisconnect(disconnectCauseFromCode(causeCode)); 438 } 439 440 /** Called when the radio indicates the connection has been disconnected */ 441 /*package*/ void 442 onDisconnect(DisconnectCause cause) { 443 this.cause = cause; 444 445 if (!disconnected) { 446 doDisconnect(); 447 if (false) Log.d(LOG_TAG, 448 "[CDMAConn] 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 (false) Log.d(LOG_TAG, 465 "[CDMAConn] onLoalDisconnect" ); 466 467 if (parent != null) { 468 parent.detach(this); 469 } 470 } 471 releaseWakeLock(); 472 } 473 474 // Returns true if state has changed, false if nothing changed 475 /*package*/ boolean 476 update (DriverCall dc) { 477 CdmaCall newParent; 478 boolean changed = false; 479 boolean wasConnectingInOrOut = isConnectingInOrOut(); 480 boolean wasHolding = (getState() == CdmaCall.State.HOLDING); 481 482 newParent = parentFromDCState(dc.state); 483 484 if (Phone.DEBUG_PHONE) log("parent= " +parent +", newParent= " + newParent); 485 486 if (!equalsHandlesNulls(address, dc.number)) { 487 if (Phone.DEBUG_PHONE) log("update: phone # changed!"); 488 address = dc.number; 489 changed = true; 490 } 491 492 // A null cnapName should be the same as "" 493 if (TextUtils.isEmpty(dc.name)) { 494 if (!TextUtils.isEmpty(cnapName)) { 495 changed = true; 496 cnapName = ""; 497 } 498 } else if (!dc.name.equals(cnapName)) { 499 changed = true; 500 cnapName = dc.name; 501 } 502 503 if (Phone.DEBUG_PHONE) log("--dssds----"+cnapName); 504 cnapNamePresentation = dc.namePresentation; 505 numberPresentation = dc.numberPresentation; 506 507 if (newParent != parent) { 508 if (parent != null) { 509 parent.detach(this); 510 } 511 newParent.attach(this, dc); 512 parent = newParent; 513 changed = true; 514 } else { 515 boolean parentStateChange; 516 parentStateChange = parent.update (this, dc); 517 changed = changed || parentStateChange; 518 } 519 520 /** Some state-transition events */ 521 522 if (Phone.DEBUG_PHONE) log( 523 "Update, wasConnectingInOrOut=" + wasConnectingInOrOut + 524 ", wasHolding=" + wasHolding + 525 ", isConnectingInOrOut=" + isConnectingInOrOut() + 526 ", changed=" + changed); 527 528 529 if (wasConnectingInOrOut && !isConnectingInOrOut()) { 530 onConnectedInOrOut(); 531 } 532 533 if (changed && !wasHolding && (getState() == CdmaCall.State.HOLDING)) { 534 // We've transitioned into HOLDING 535 onStartedHolding(); 536 } 537 538 return changed; 539 } 540 541 /** 542 * Called when this Connection is in the foregroundCall 543 * when a dial is initiated. 544 * We know we're ACTIVE, and we know we're going to end up 545 * HOLDING in the backgroundCall 546 */ 547 void 548 fakeHoldBeforeDial() { 549 if (parent != null) { 550 parent.detach(this); 551 } 552 553 parent = owner.backgroundCall; 554 parent.attachFake(this, CdmaCall.State.HOLDING); 555 556 onStartedHolding(); 557 } 558 559 /*package*/ int 560 getCDMAIndex() throws CallStateException { 561 if (index >= 0) { 562 return index + 1; 563 } else { 564 throw new CallStateException ("CDMA connection index not assigned"); 565 } 566 } 567 568 /** 569 * An incoming or outgoing call has connected 570 */ 571 void 572 onConnectedInOrOut() { 573 connectTime = System.currentTimeMillis(); 574 connectTimeReal = SystemClock.elapsedRealtime(); 575 duration = 0; 576 577 // bug #678474: incoming call interpreted as missed call, even though 578 // it sounds like the user has picked up the call. 579 if (Phone.DEBUG_PHONE) { 580 log("onConnectedInOrOut: connectTime=" + connectTime); 581 } 582 583 if (!isIncoming) { 584 // outgoing calls only 585 processNextPostDialChar(); 586 } else { 587 // Only release wake lock for incoming calls, for outgoing calls the wake lock 588 // will be released after any pause-dial is completed 589 releaseWakeLock(); 590 } 591 } 592 593 private void 594 doDisconnect() { 595 index = -1; 596 disconnectTime = System.currentTimeMillis(); 597 duration = SystemClock.elapsedRealtime() - connectTimeReal; 598 disconnected = true; 599 } 600 601 private void 602 onStartedHolding() { 603 holdingStartTime = SystemClock.elapsedRealtime(); 604 } 605 /** 606 * Performs the appropriate action for a post-dial char, but does not 607 * notify application. returns false if the character is invalid and 608 * should be ignored 609 */ 610 private boolean 611 processPostDialChar(char c) { 612 if (PhoneNumberUtils.is12Key(c)) { 613 owner.cm.sendDtmf(c, h.obtainMessage(EVENT_DTMF_DONE)); 614 } else if (c == PhoneNumberUtils.PAUSE) { 615 setPostDialState(PostDialState.PAUSE); 616 617 // Upon occurrences of the separator, the UE shall 618 // pause again for 2 seconds before sending any 619 // further DTMF digits. 620 h.sendMessageDelayed(h.obtainMessage(EVENT_PAUSE_DONE), 621 PAUSE_DELAY_MILLIS); 622 } else if (c == PhoneNumberUtils.WAIT) { 623 setPostDialState(PostDialState.WAIT); 624 } else if (c == PhoneNumberUtils.WILD) { 625 setPostDialState(PostDialState.WILD); 626 } else { 627 return false; 628 } 629 630 return true; 631 } 632 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 Log.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 //Log.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 Log.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 Log.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