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 { MyHandler(Looper l)100 MyHandler(Looper l) {super(l);} 101 102 public void handleMessage(Message msg)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*/ CdmaConnection(Context context, DriverCall dc, CdmaCallTracker ct, int index)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*/ CdmaConnection(Context context, String dialString, CdmaCallTracker ct, CdmaCall parent)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.extractNetworkPortion(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*/ CdmaConnection(Context context, CdmaCallWaitingNotification cw, CdmaCallTracker ct, CdmaCall parent)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 dispose()200 public void dispose() { 201 } 202 203 static boolean equalsHandlesNulls(Object a, Object b)204 equalsHandlesNulls (Object a, Object b) { 205 return (a == null) ? (b == null) : a.equals (b); 206 } 207 208 /*package*/ boolean compareTo(DriverCall c)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 getOrigDialString()225 public String getOrigDialString(){ 226 return dialString; 227 } 228 getAddress()229 public String getAddress() { 230 return address; 231 } 232 getCnapName()233 public String getCnapName() { 234 return cnapName; 235 } 236 getCnapNamePresentation()237 public int getCnapNamePresentation() { 238 return cnapNamePresentation; 239 } 240 getCall()241 public CdmaCall getCall() { 242 return parent; 243 } 244 getCreateTime()245 public long getCreateTime() { 246 return createTime; 247 } 248 getConnectTime()249 public long getConnectTime() { 250 return connectTime; 251 } 252 getDisconnectTime()253 public long getDisconnectTime() { 254 return disconnectTime; 255 } 256 getDurationMillis()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 getHoldDurationMillis()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 getDisconnectCause()276 public DisconnectCause getDisconnectCause() { 277 return cause; 278 } 279 isIncoming()280 public boolean isIncoming() { 281 return isIncoming; 282 } 283 getState()284 public CdmaCall.State getState() { 285 if (disconnected) { 286 return CdmaCall.State.DISCONNECTED; 287 } else { 288 return super.getState(); 289 } 290 } 291 hangup()292 public void hangup() throws CallStateException { 293 if (!disconnected) { 294 owner.hangup(this); 295 } else { 296 throw new CallStateException ("disconnected"); 297 } 298 } 299 separate()300 public void separate() throws CallStateException { 301 if (!disconnected) { 302 owner.separate(this); 303 } else { 304 throw new CallStateException ("disconnected"); 305 } 306 } 307 getPostDialState()308 public PostDialState getPostDialState() { 309 return postDialState; 310 } 311 proceedAfterWaitChar()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 proceedAfterWildChar(String str)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 cancelPostDial()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 onHangupLocal()381 onHangupLocal() { 382 cause = DisconnectCause.LOCAL; 383 } 384 385 DisconnectCause disconnectCauseFromCode(int causeCode)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 onRemoteDisconnect(int causeCode)445 onRemoteDisconnect(int causeCode) { 446 onDisconnect(disconnectCauseFromCode(causeCode)); 447 } 448 449 /** Called when the radio indicates the connection has been disconnected */ 450 /*package*/ void onDisconnect(DisconnectCause cause)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 onLocalDisconnect()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 update(DriverCall dc)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 fakeHoldBeforeDial()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 getCDMAIndex()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 onConnectedInOrOut()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 } 596 releaseWakeLock(); 597 } 598 599 private void doDisconnect()600 doDisconnect() { 601 index = -1; 602 disconnectTime = System.currentTimeMillis(); 603 duration = SystemClock.elapsedRealtime() - connectTimeReal; 604 disconnected = true; 605 } 606 607 private void onStartedHolding()608 onStartedHolding() { 609 holdingStartTime = SystemClock.elapsedRealtime(); 610 } 611 /** 612 * Performs the appropriate action for a post-dial char, but does not 613 * notify application. returns false if the character is invalid and 614 * should be ignored 615 */ 616 private boolean processPostDialChar(char c)617 processPostDialChar(char c) { 618 if (PhoneNumberUtils.is12Key(c)) { 619 owner.cm.sendDtmf(c, h.obtainMessage(EVENT_DTMF_DONE)); 620 } else if (c == PhoneNumberUtils.PAUSE) { 621 setPostDialState(PostDialState.PAUSE); 622 623 // Upon occurrences of the separator, the UE shall 624 // pause again for 2 seconds before sending any 625 // further DTMF digits. 626 h.sendMessageDelayed(h.obtainMessage(EVENT_PAUSE_DONE), 627 PAUSE_DELAY_MILLIS); 628 } else if (c == PhoneNumberUtils.WAIT) { 629 setPostDialState(PostDialState.WAIT); 630 } else if (c == PhoneNumberUtils.WILD) { 631 setPostDialState(PostDialState.WILD); 632 } else { 633 return false; 634 } 635 636 return true; 637 } 638 getRemainingPostDialString()639 public String getRemainingPostDialString() { 640 if (postDialState == PostDialState.CANCELLED 641 || postDialState == PostDialState.COMPLETE 642 || postDialString == null 643 || postDialString.length() <= nextPostDialChar) { 644 return ""; 645 } 646 647 String subStr = postDialString.substring(nextPostDialChar); 648 if (subStr != null) { 649 int wIndex = subStr.indexOf(PhoneNumberUtils.WAIT); 650 int pIndex = subStr.indexOf(PhoneNumberUtils.PAUSE); 651 652 if (wIndex > 0 && (wIndex < pIndex || pIndex <= 0)) { 653 subStr = subStr.substring(0, wIndex); 654 } else if (pIndex > 0) { 655 subStr = subStr.substring(0, pIndex); 656 } 657 } 658 return subStr; 659 } 660 updateParent(CdmaCall oldParent, CdmaCall newParent)661 public void updateParent(CdmaCall oldParent, CdmaCall newParent){ 662 if (newParent != oldParent) { 663 if (oldParent != null) { 664 oldParent.detach(this); 665 } 666 newParent.attachFake(this, CdmaCall.State.ACTIVE); 667 parent = newParent; 668 } 669 } 670 671 @Override finalize()672 protected void finalize() 673 { 674 /** 675 * It is understood that This finializer is not guaranteed 676 * to be called and the release lock call is here just in 677 * case there is some path that doesn't call onDisconnect 678 * and or onConnectedInOrOut. 679 */ 680 if (mPartialWakeLock.isHeld()) { 681 Log.e(LOG_TAG, "[CdmaConn] UNEXPECTED; mPartialWakeLock is held when finalizing."); 682 } 683 releaseWakeLock(); 684 } 685 processNextPostDialChar()686 void processNextPostDialChar() { 687 char c = 0; 688 Registrant postDialHandler; 689 690 if (postDialState == PostDialState.CANCELLED) { 691 //Log.v("CDMA", "##### processNextPostDialChar: postDialState == CANCELLED, bail"); 692 return; 693 } 694 695 if (postDialString == null || 696 postDialString.length() <= nextPostDialChar) { 697 setPostDialState(PostDialState.COMPLETE); 698 699 // notifyMessage.arg1 is 0 on complete 700 c = 0; 701 } else { 702 boolean isValid; 703 704 setPostDialState(PostDialState.STARTED); 705 706 c = postDialString.charAt(nextPostDialChar++); 707 708 isValid = processPostDialChar(c); 709 710 if (!isValid) { 711 // Will call processNextPostDialChar 712 h.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget(); 713 // Don't notify application 714 Log.e("CDMA", "processNextPostDialChar: c=" + c + " isn't valid!"); 715 return; 716 } 717 } 718 719 postDialHandler = owner.phone.mPostDialHandler; 720 721 Message notifyMessage; 722 723 if (postDialHandler != null && 724 (notifyMessage = postDialHandler.messageForRegistrant()) != null) { 725 // The AsyncResult.result is the Connection object 726 PostDialState state = postDialState; 727 AsyncResult ar = AsyncResult.forMessage(notifyMessage); 728 ar.result = this; 729 ar.userObj = state; 730 731 // arg1 is the character that was/is being processed 732 notifyMessage.arg1 = c; 733 734 notifyMessage.sendToTarget(); 735 } 736 } 737 738 739 /** "connecting" means "has never been ACTIVE" for both incoming 740 * and outgoing calls 741 */ 742 private boolean isConnectingInOrOut()743 isConnectingInOrOut() { 744 return parent == null || parent == owner.ringingCall 745 || parent.state == CdmaCall.State.DIALING 746 || parent.state == CdmaCall.State.ALERTING; 747 } 748 749 private CdmaCall parentFromDCState(DriverCall.State state)750 parentFromDCState (DriverCall.State state) { 751 switch (state) { 752 case ACTIVE: 753 case DIALING: 754 case ALERTING: 755 return owner.foregroundCall; 756 //break; 757 758 case HOLDING: 759 return owner.backgroundCall; 760 //break; 761 762 case INCOMING: 763 case WAITING: 764 return owner.ringingCall; 765 //break; 766 767 default: 768 throw new RuntimeException("illegal call state: " + state); 769 } 770 } 771 772 /** 773 * Set post dial state and acquire wake lock while switching to "started" 774 * state, the wake lock will be released if state switches out of "started" 775 * state or after WAKE_LOCK_TIMEOUT_MILLIS. 776 * @param s new PostDialState 777 */ setPostDialState(PostDialState s)778 private void setPostDialState(PostDialState s) { 779 if (postDialState != PostDialState.STARTED 780 && s == PostDialState.STARTED) { 781 acquireWakeLock(); 782 Message msg = h.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT); 783 h.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS); 784 } else if (postDialState == PostDialState.STARTED 785 && s != PostDialState.STARTED) { 786 h.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); 787 releaseWakeLock(); 788 } 789 postDialState = s; 790 } 791 createWakeLock(Context context)792 private void createWakeLock(Context context) { 793 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 794 mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); 795 } 796 acquireWakeLock()797 private void acquireWakeLock() { 798 log("acquireWakeLock"); 799 mPartialWakeLock.acquire(); 800 } 801 releaseWakeLock()802 private void releaseWakeLock() { 803 synchronized (mPartialWakeLock) { 804 if (mPartialWakeLock.isHeld()) { 805 log("releaseWakeLock"); 806 mPartialWakeLock.release(); 807 } 808 } 809 } 810 isPause(char c)811 private static boolean isPause(char c) { 812 return c == PhoneNumberUtils.PAUSE; 813 } 814 isWait(char c)815 private static boolean isWait(char c) { 816 return c == PhoneNumberUtils.WAIT; 817 } 818 819 // This function is to find the next PAUSE character index if 820 // multiple pauses in a row. Otherwise it finds the next non PAUSE or 821 // non WAIT character index. 822 private static int findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex)823 findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex) { 824 boolean wMatched = isWait(phoneNumber.charAt(currIndex)); 825 int index = currIndex + 1; 826 int length = phoneNumber.length(); 827 while (index < length) { 828 char cNext = phoneNumber.charAt(index); 829 // if there is any W inside P/W sequence,mark it 830 if (isWait(cNext)) { 831 wMatched = true; 832 } 833 // if any characters other than P/W chars after P/W sequence 834 // we break out the loop and append the correct 835 if (!isWait(cNext) && !isPause(cNext)) { 836 break; 837 } 838 index++; 839 } 840 841 // It means the PAUSE character(s) is in the middle of dial string 842 // and it needs to be handled one by one. 843 if ((index < length) && (index > (currIndex + 1)) && 844 ((wMatched == false) && isPause(phoneNumber.charAt(currIndex)))) { 845 return (currIndex + 1); 846 } 847 return index; 848 } 849 850 // This function returns either PAUSE or WAIT character to append. 851 // It is based on the next non PAUSE/WAIT character in the phoneNumber and the 852 // index for the current PAUSE/WAIT character 853 private static char findPOrWCharToAppend(String phoneNumber, int currPwIndex, int nextNonPwCharIndex)854 findPOrWCharToAppend(String phoneNumber, int currPwIndex, int nextNonPwCharIndex) { 855 char c = phoneNumber.charAt(currPwIndex); 856 char ret; 857 858 // Append the PW char 859 ret = (isPause(c)) ? PhoneNumberUtils.PAUSE : PhoneNumberUtils.WAIT; 860 861 // If the nextNonPwCharIndex is greater than currPwIndex + 1, 862 // it means the PW sequence contains not only P characters. 863 // Since for the sequence that only contains P character, 864 // the P character is handled one by one, the nextNonPwCharIndex 865 // equals to currPwIndex + 1. 866 // In this case, skip P, append W. 867 if (nextNonPwCharIndex > (currPwIndex + 1)) { 868 ret = PhoneNumberUtils.WAIT; 869 } 870 return ret; 871 } 872 873 /** 874 * format original dial string 875 * 1) convert international dialing prefix "+" to 876 * string specified per region 877 * 878 * 2) handle corner cases for PAUSE/WAIT dialing: 879 * 880 * If PAUSE/WAIT sequence at the end, ignore them. 881 * 882 * If consecutive PAUSE/WAIT sequence in the middle of the string, 883 * and if there is any WAIT in PAUSE/WAIT sequence, treat them like WAIT. 884 */ formatDialString(String phoneNumber)885 public static String formatDialString(String phoneNumber) { 886 /** 887 * TODO(cleanup): This function should move to PhoneNumberUtils, and 888 * tests should be added. 889 */ 890 891 if (phoneNumber == null) { 892 return null; 893 } 894 int length = phoneNumber.length(); 895 StringBuilder ret = new StringBuilder(); 896 char c; 897 int currIndex = 0; 898 899 while (currIndex < length) { 900 c = phoneNumber.charAt(currIndex); 901 if (isPause(c) || isWait(c)) { 902 if (currIndex < length - 1) { 903 // if PW not at the end 904 int nextIndex = findNextPCharOrNonPOrNonWCharIndex(phoneNumber, currIndex); 905 // If there is non PW char following PW sequence 906 if (nextIndex < length) { 907 char pC = findPOrWCharToAppend(phoneNumber, currIndex, nextIndex); 908 ret.append(pC); 909 // If PW char sequence has more than 2 PW characters, 910 // skip to the last PW character since the sequence already be 911 // converted to WAIT character 912 if (nextIndex > (currIndex + 1)) { 913 currIndex = nextIndex - 1; 914 } 915 } else if (nextIndex == length) { 916 // It means PW characters at the end, ignore 917 currIndex = length - 1; 918 } 919 } 920 } else { 921 ret.append(c); 922 } 923 currIndex++; 924 } 925 return PhoneNumberUtils.cdmaCheckAndProcessPlusCode(ret.toString()); 926 } 927 log(String msg)928 private void log(String msg) { 929 Log.d(LOG_TAG, "[CDMAConn] " + msg); 930 } 931 932 @Override getNumberPresentation()933 public int getNumberPresentation() { 934 return numberPresentation; 935 } 936 } 937