ImsPhone.java revision 4be56374921b52d54b80889540d982f39d26e3ab
1/* 2 * Copyright (C) 2013 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.imsphone; 18 19import android.app.ActivityManagerNative; 20import android.content.Context; 21import android.content.Intent; 22import android.os.AsyncResult; 23import android.os.Handler; 24import android.os.Message; 25import android.os.PowerManager; 26import android.os.Registrant; 27import android.os.RegistrantList; 28import android.os.PowerManager.WakeLock; 29import android.os.SystemProperties; 30import android.os.UserHandle; 31 32import android.telephony.PhoneNumberUtils; 33import android.telephony.ServiceState; 34import android.telephony.Rlog; 35import android.telephony.SubscriptionManager; 36import android.text.TextUtils; 37 38import com.android.ims.ImsCallForwardInfo; 39import com.android.ims.ImsCallProfile; 40import com.android.ims.ImsEcbm; 41import com.android.ims.ImsEcbmStateListener; 42import com.android.ims.ImsException; 43import com.android.ims.ImsReasonInfo; 44import com.android.ims.ImsSsInfo; 45import com.android.ims.ImsUtInterface; 46 47import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC; 48import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC; 49import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH; 50import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC; 51import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr; 52import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL; 53import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO; 54import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT; 55 56import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE; 57import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE; 58import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE; 59import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION; 60import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL; 61import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL; 62import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY; 63import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE; 64import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY; 65import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL; 66import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE; 67import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE; 68 69import com.android.internal.telephony.Call; 70import com.android.internal.telephony.CallForwardInfo; 71import com.android.internal.telephony.CallStateException; 72import com.android.internal.telephony.CallTracker; 73import com.android.internal.telephony.CommandException; 74import com.android.internal.telephony.CommandsInterface; 75import com.android.internal.telephony.Connection; 76import com.android.internal.telephony.Phone; 77import com.android.internal.telephony.PhoneBase; 78import com.android.internal.telephony.PhoneConstants; 79import com.android.internal.telephony.PhoneNotifier; 80import com.android.internal.telephony.TelephonyIntents; 81import com.android.internal.telephony.TelephonyProperties; 82import com.android.internal.telephony.cdma.CDMAPhone; 83import com.android.internal.telephony.gsm.GSMPhone; 84import com.android.internal.telephony.uicc.IccRecords; 85 86import java.util.ArrayList; 87import java.util.List; 88 89/** 90 * {@hide} 91 */ 92public class ImsPhone extends ImsPhoneBase { 93 private static final String LOG_TAG = "ImsPhone"; 94 private static final boolean DBG = true; 95 private static final boolean VDBG = false; // STOPSHIP if true 96 97 protected static final int EVENT_SET_CALL_BARRING_DONE = EVENT_LAST + 1; 98 protected static final int EVENT_GET_CALL_BARRING_DONE = EVENT_LAST + 2; 99 protected static final int EVENT_SET_CALL_WAITING_DONE = EVENT_LAST + 3; 100 protected static final int EVENT_GET_CALL_WAITING_DONE = EVENT_LAST + 4; 101 102 public static final String CS_FALLBACK = "cs_fallback"; 103 104 static final int RESTART_ECM_TIMER = 0; // restart Ecm timer 105 static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer 106 107 // Default Emergency Callback Mode exit timer 108 private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000; 109 110 // Instance Variables 111 PhoneBase mDefaultPhone; 112 ImsPhoneCallTracker mCT; 113 ArrayList <ImsPhoneMmiCode> mPendingMMIs = new ArrayList<ImsPhoneMmiCode>(); 114 115 Registrant mPostDialHandler; 116 ServiceState mSS = new ServiceState(); 117 118 // To redial silently through GSM or CDMA when dialing through IMS fails 119 private String mLastDialString; 120 121 WakeLock mWakeLock; 122 protected boolean mIsPhoneInEcmState; 123 124 // mEcmExitRespRegistrant is informed after the phone has been exited the emergency 125 // callback mode keep track of if phone is in emergency callback mode 126 private Registrant mEcmExitRespRegistrant; 127 128 private final RegistrantList mSilentRedialRegistrants = new RegistrantList(); 129 130 // A runnable which is used to automatically exit from Ecm after a period of time. 131 private Runnable mExitEcmRunnable = new Runnable() { 132 @Override 133 public void run() { 134 exitEmergencyCallbackMode(); 135 } 136 }; 137 138 // Create Cf (Call forward) so that dialling number & 139 // mIsCfu (true if reason is call forward unconditional) 140 // mOnComplete (Message object passed by client) can be packed & 141 // given as a single Cf object as user data to UtInterface. 142 private static class Cf { 143 final String mSetCfNumber; 144 final Message mOnComplete; 145 final boolean mIsCfu; 146 147 Cf(String cfNumber, boolean isCfu, Message onComplete) { 148 mSetCfNumber = cfNumber; 149 mIsCfu = isCfu; 150 mOnComplete = onComplete; 151 } 152 } 153 154 // Constructors 155 156 ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone) { 157 super("ImsPhone", context, notifier); 158 159 mDefaultPhone = (PhoneBase) defaultPhone; 160 mCT = new ImsPhoneCallTracker(this); 161 mSS.setStateOff(); 162 163 mPhoneId = mDefaultPhone.getPhoneId(); 164 165 // This is needed to handle phone process crashes 166 // Same property is used for both CDMA & IMS phone. 167 mIsPhoneInEcmState = SystemProperties.getBoolean( 168 TelephonyProperties.PROPERTY_INECM_MODE, false); 169 170 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 171 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); 172 mWakeLock.setReferenceCounted(false); 173 } 174 175 public void updateParentPhone(PhoneBase parentPhone) { 176 // synchronization is managed at the PhoneBase scope (which calls this function) 177 mDefaultPhone = parentPhone; 178 mPhoneId = mDefaultPhone.getPhoneId(); 179 } 180 181 @Override 182 public void dispose() { 183 Rlog.d(LOG_TAG, "dispose"); 184 // Nothing to dispose in PhoneBase 185 //super.dispose(); 186 mPendingMMIs.clear(); 187 mCT.dispose(); 188 189 //Force all referenced classes to unregister their former registered events 190 } 191 192 @Override 193 public void removeReferences() { 194 Rlog.d(LOG_TAG, "removeReferences"); 195 super.removeReferences(); 196 197 mCT = null; 198 mSS = null; 199 } 200 201 @Override 202 public ServiceState 203 getServiceState() { 204 return mSS; 205 } 206 207 /* package */ void setServiceState(int state) { 208 mSS.setState(state); 209 } 210 211 @Override 212 public CallTracker getCallTracker() { 213 return mCT; 214 } 215 216 @Override 217 public List<? extends ImsPhoneMmiCode> 218 getPendingMmiCodes() { 219 return mPendingMMIs; 220 } 221 222 223 @Override 224 public void 225 acceptCall(int videoState) throws CallStateException { 226 mCT.acceptCall(videoState); 227 } 228 229 @Override 230 public void 231 rejectCall() throws CallStateException { 232 mCT.rejectCall(); 233 } 234 235 @Override 236 public void 237 switchHoldingAndActive() throws CallStateException { 238 mCT.switchWaitingOrHoldingAndActive(); 239 } 240 241 @Override 242 public boolean canConference() { 243 return mCT.canConference(); 244 } 245 246 public boolean canDial() { 247 return mCT.canDial(); 248 } 249 250 @Override 251 public void conference() { 252 mCT.conference(); 253 } 254 255 @Override 256 public void clearDisconnected() { 257 mCT.clearDisconnected(); 258 } 259 260 @Override 261 public boolean canTransfer() { 262 return mCT.canTransfer(); 263 } 264 265 @Override 266 public void explicitCallTransfer() { 267 mCT.explicitCallTransfer(); 268 } 269 270 @Override 271 public ImsPhoneCall 272 getForegroundCall() { 273 return mCT.mForegroundCall; 274 } 275 276 @Override 277 public ImsPhoneCall 278 getBackgroundCall() { 279 return mCT.mBackgroundCall; 280 } 281 282 @Override 283 public ImsPhoneCall 284 getRingingCall() { 285 return mCT.mRingingCall; 286 } 287 288 private boolean handleCallDeflectionIncallSupplementaryService( 289 String dialString) { 290 if (dialString.length() > 1) { 291 return false; 292 } 293 294 if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) { 295 if (DBG) Rlog.d(LOG_TAG, "MmiCode 0: rejectCall"); 296 try { 297 mCT.rejectCall(); 298 } catch (CallStateException e) { 299 if (DBG) Rlog.d(LOG_TAG, "reject failed", e); 300 notifySuppServiceFailed(Phone.SuppService.REJECT); 301 } 302 } else if (getBackgroundCall().getState() != ImsPhoneCall.State.IDLE) { 303 if (DBG) Rlog.d(LOG_TAG, "MmiCode 0: hangupWaitingOrBackground"); 304 try { 305 mCT.hangup(getBackgroundCall()); 306 } catch (CallStateException e) { 307 if (DBG) Rlog.d(LOG_TAG, "hangup failed", e); 308 } 309 } 310 311 return true; 312 } 313 314 315 private boolean handleCallWaitingIncallSupplementaryService( 316 String dialString) { 317 int len = dialString.length(); 318 319 if (len > 2) { 320 return false; 321 } 322 323 ImsPhoneCall call = getForegroundCall(); 324 325 try { 326 if (len > 1) { 327 if (DBG) Rlog.d(LOG_TAG, "not support 1X SEND"); 328 notifySuppServiceFailed(Phone.SuppService.HANGUP); 329 } else { 330 if (call.getState() != ImsPhoneCall.State.IDLE) { 331 if (DBG) Rlog.d(LOG_TAG, "MmiCode 1: hangup foreground"); 332 mCT.hangup(call); 333 } else { 334 if (DBG) Rlog.d(LOG_TAG, "MmiCode 1: switchWaitingOrHoldingAndActive"); 335 mCT.switchWaitingOrHoldingAndActive(); 336 } 337 } 338 } catch (CallStateException e) { 339 if (DBG) Rlog.d(LOG_TAG, "hangup failed", e); 340 notifySuppServiceFailed(Phone.SuppService.HANGUP); 341 } 342 343 return true; 344 } 345 346 private boolean handleCallHoldIncallSupplementaryService(String dialString) { 347 int len = dialString.length(); 348 349 if (len > 2) { 350 return false; 351 } 352 353 ImsPhoneCall call = getForegroundCall(); 354 355 if (len > 1) { 356 if (DBG) Rlog.d(LOG_TAG, "separate not supported"); 357 notifySuppServiceFailed(Phone.SuppService.SEPARATE); 358 } else { 359 try { 360 if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) { 361 if (DBG) Rlog.d(LOG_TAG, "MmiCode 2: accept ringing call"); 362 mCT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE); 363 } else { 364 if (DBG) Rlog.d(LOG_TAG, "MmiCode 2: switchWaitingOrHoldingAndActive"); 365 mCT.switchWaitingOrHoldingAndActive(); 366 } 367 } catch (CallStateException e) { 368 if (DBG) Rlog.d(LOG_TAG, "switch failed", e); 369 notifySuppServiceFailed(Phone.SuppService.SWITCH); 370 } 371 } 372 373 return true; 374 } 375 376 private boolean handleMultipartyIncallSupplementaryService( 377 String dialString) { 378 if (dialString.length() > 1) { 379 return false; 380 } 381 382 if (DBG) Rlog.d(LOG_TAG, "MmiCode 3: merge calls"); 383 conference(); 384 return true; 385 } 386 387 private boolean handleEctIncallSupplementaryService(String dialString) { 388 389 int len = dialString.length(); 390 391 if (len != 1) { 392 return false; 393 } 394 395 if (DBG) Rlog.d(LOG_TAG, "MmiCode 4: not support explicit call transfer"); 396 notifySuppServiceFailed(Phone.SuppService.TRANSFER); 397 return true; 398 } 399 400 private boolean handleCcbsIncallSupplementaryService(String dialString) { 401 if (dialString.length() > 1) { 402 return false; 403 } 404 405 Rlog.i(LOG_TAG, "MmiCode 5: CCBS not supported!"); 406 // Treat it as an "unknown" service. 407 notifySuppServiceFailed(Phone.SuppService.UNKNOWN); 408 return true; 409 } 410 411 @Override 412 public boolean handleInCallMmiCommands(String dialString) { 413 if (!isInCall()) { 414 return false; 415 } 416 417 if (TextUtils.isEmpty(dialString)) { 418 return false; 419 } 420 421 boolean result = false; 422 char ch = dialString.charAt(0); 423 switch (ch) { 424 case '0': 425 result = handleCallDeflectionIncallSupplementaryService( 426 dialString); 427 break; 428 case '1': 429 result = handleCallWaitingIncallSupplementaryService( 430 dialString); 431 break; 432 case '2': 433 result = handleCallHoldIncallSupplementaryService(dialString); 434 break; 435 case '3': 436 result = handleMultipartyIncallSupplementaryService(dialString); 437 break; 438 case '4': 439 result = handleEctIncallSupplementaryService(dialString); 440 break; 441 case '5': 442 result = handleCcbsIncallSupplementaryService(dialString); 443 break; 444 default: 445 break; 446 } 447 448 return result; 449 } 450 451 boolean isInCall() { 452 ImsPhoneCall.State foregroundCallState = getForegroundCall().getState(); 453 ImsPhoneCall.State backgroundCallState = getBackgroundCall().getState(); 454 ImsPhoneCall.State ringingCallState = getRingingCall().getState(); 455 456 return (foregroundCallState.isAlive() || 457 backgroundCallState.isAlive() || 458 ringingCallState.isAlive()); 459 } 460 461 void notifyNewRingingConnection(Connection c) { 462 mDefaultPhone.notifyNewRingingConnectionP(c); 463 } 464 465 466 @Override 467 public Connection 468 dial(String dialString, int videoState) throws CallStateException { 469 return dialInternal(dialString, videoState); 470 } 471 472 protected Connection dialInternal(String dialString, int videoState) 473 throws CallStateException { 474 // Need to make sure dialString gets parsed properly 475 String newDialString = PhoneNumberUtils.stripSeparators(dialString); 476 477 // handle in-call MMI first if applicable 478 if (handleInCallMmiCommands(newDialString)) { 479 return null; 480 } 481 482 if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { 483 return mCT.dial(dialString, videoState); 484 } 485 486 // Only look at the Network portion for mmi 487 String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString); 488 ImsPhoneMmiCode mmi = 489 ImsPhoneMmiCode.newFromDialString(networkPortion, this); 490 if (DBG) Rlog.d(LOG_TAG, 491 "dialing w/ mmi '" + mmi + "'..."); 492 493 if (mmi == null) { 494 return mCT.dial(dialString, videoState); 495 } else if (mmi.isTemporaryModeCLIR()) { 496 return mCT.dial(mmi.getDialingNumber(), mmi.getCLIRMode(), videoState); 497 } else if (!mmi.isSupportedOverImsPhone()) { 498 // If the mmi is not supported by IMS service, 499 // try to initiate dialing with default phone 500 throw new CallStateException(CS_FALLBACK); 501 } else { 502 mPendingMMIs.add(mmi); 503 mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); 504 mmi.processCode(); 505 506 return null; 507 } 508 } 509 510 @Override 511 public void 512 sendDtmf(char c) { 513 if (!PhoneNumberUtils.is12Key(c)) { 514 Rlog.e(LOG_TAG, 515 "sendDtmf called with invalid character '" + c + "'"); 516 } else { 517 if (mCT.mState == PhoneConstants.State.OFFHOOK) { 518 mCT.sendDtmf(c); 519 } 520 } 521 } 522 523 @Override 524 public void 525 startDtmf(char c) { 526 if (!(PhoneNumberUtils.is12Key(c) || (c >= 'A' && c <= 'D'))) { 527 Rlog.e(LOG_TAG, 528 "startDtmf called with invalid character '" + c + "'"); 529 } else { 530 mCT.startDtmf(c); 531 } 532 } 533 534 @Override 535 public void 536 stopDtmf() { 537 mCT.stopDtmf(); 538 } 539 540 @Override 541 public void setOnPostDialCharacter(Handler h, int what, Object obj) { 542 mPostDialHandler = new Registrant(h, what, obj); 543 } 544 545 /*package*/ void notifyIncomingRing() { 546 if (DBG) Rlog.d(LOG_TAG, "notifyIncomingRing"); 547 AsyncResult ar = new AsyncResult(null, null, null); 548 sendMessage(obtainMessage(EVENT_CALL_RING, ar)); 549 } 550 551 @Override 552 public void setMute(boolean muted) { 553 mCT.setMute(muted); 554 } 555 556 @Override 557 public void setUiTTYMode(int uiTtyMode, Message onComplete) { 558 mCT.setUiTTYMode(uiTtyMode, onComplete); 559 } 560 561 @Override 562 public boolean getMute() { 563 return mCT.getMute(); 564 } 565 566 @Override 567 public PhoneConstants.State getState() { 568 return mCT.mState; 569 } 570 571 private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) { 572 switch (commandInterfaceCFReason) { 573 case CF_REASON_UNCONDITIONAL: 574 case CF_REASON_BUSY: 575 case CF_REASON_NO_REPLY: 576 case CF_REASON_NOT_REACHABLE: 577 case CF_REASON_ALL: 578 case CF_REASON_ALL_CONDITIONAL: 579 return true; 580 default: 581 return false; 582 } 583 } 584 585 private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) { 586 switch (commandInterfaceCFAction) { 587 case CF_ACTION_DISABLE: 588 case CF_ACTION_ENABLE: 589 case CF_ACTION_REGISTRATION: 590 case CF_ACTION_ERASURE: 591 return true; 592 default: 593 return false; 594 } 595 } 596 597 private boolean isCfEnable(int action) { 598 return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION); 599 } 600 601 private int getConditionFromCFReason(int reason) { 602 switch(reason) { 603 case CF_REASON_UNCONDITIONAL: return ImsUtInterface.CDIV_CF_UNCONDITIONAL; 604 case CF_REASON_BUSY: return ImsUtInterface.CDIV_CF_BUSY; 605 case CF_REASON_NO_REPLY: return ImsUtInterface.CDIV_CF_NO_REPLY; 606 case CF_REASON_NOT_REACHABLE: return ImsUtInterface.CDIV_CF_NOT_REACHABLE; 607 case CF_REASON_ALL: return ImsUtInterface.CDIV_CF_ALL; 608 case CF_REASON_ALL_CONDITIONAL: return ImsUtInterface.CDIV_CF_ALL_CONDITIONAL; 609 default: 610 break; 611 } 612 613 return ImsUtInterface.INVALID; 614 } 615 616 private int getCFReasonFromCondition(int condition) { 617 switch(condition) { 618 case ImsUtInterface.CDIV_CF_UNCONDITIONAL: return CF_REASON_UNCONDITIONAL; 619 case ImsUtInterface.CDIV_CF_BUSY: return CF_REASON_BUSY; 620 case ImsUtInterface.CDIV_CF_NO_REPLY: return CF_REASON_NO_REPLY; 621 case ImsUtInterface.CDIV_CF_NOT_REACHABLE: return CF_REASON_NOT_REACHABLE; 622 case ImsUtInterface.CDIV_CF_ALL: return CF_REASON_ALL; 623 case ImsUtInterface.CDIV_CF_ALL_CONDITIONAL: return CF_REASON_ALL_CONDITIONAL; 624 default: 625 break; 626 } 627 628 return CF_REASON_NOT_REACHABLE; 629 } 630 631 private int getActionFromCFAction(int action) { 632 switch(action) { 633 case CF_ACTION_DISABLE: return ImsUtInterface.ACTION_DEACTIVATION; 634 case CF_ACTION_ENABLE: return ImsUtInterface.ACTION_ACTIVATION; 635 case CF_ACTION_ERASURE: return ImsUtInterface.ACTION_ERASURE; 636 case CF_ACTION_REGISTRATION: return ImsUtInterface.ACTION_REGISTRATION; 637 default: 638 break; 639 } 640 641 return ImsUtInterface.INVALID; 642 } 643 644 @Override 645 public void getCallForwardingOption(int commandInterfaceCFReason, 646 Message onComplete) { 647 if (DBG) Rlog.d(LOG_TAG, "getCallForwardingOption reason=" + commandInterfaceCFReason); 648 if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) { 649 if (DBG) Rlog.d(LOG_TAG, "requesting call forwarding query."); 650 Message resp; 651 resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete); 652 653 try { 654 ImsUtInterface ut = mCT.getUtInterface(); 655 ut.queryCallForward(getConditionFromCFReason(commandInterfaceCFReason),null,resp); 656 } catch (ImsException e) { 657 sendErrorResponse(onComplete, e); 658 } 659 } else if (onComplete != null) { 660 sendErrorResponse(onComplete); 661 } 662 } 663 664 @Override 665 public void setCallForwardingOption(int commandInterfaceCFAction, 666 int commandInterfaceCFReason, 667 String dialingNumber, 668 int timerSeconds, 669 Message onComplete) { 670 if (DBG) Rlog.d(LOG_TAG, "setCallForwardingOption action=" + commandInterfaceCFAction 671 + ", reason=" + commandInterfaceCFReason); 672 if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) && 673 (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) { 674 Message resp; 675 Cf cf = new Cf(dialingNumber, 676 (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL ? true : false), 677 onComplete); 678 resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE, 679 isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, cf); 680 681 try { 682 ImsUtInterface ut = mCT.getUtInterface(); 683 ut.updateCallForward(getActionFromCFAction(commandInterfaceCFAction), 684 getConditionFromCFReason(commandInterfaceCFReason), 685 dialingNumber, 686 timerSeconds, 687 onComplete); 688 } catch (ImsException e) { 689 sendErrorResponse(onComplete, e); 690 } 691 } else if (onComplete != null) { 692 sendErrorResponse(onComplete); 693 } 694 } 695 696 @Override 697 public void getCallWaiting(Message onComplete) { 698 if (DBG) Rlog.d(LOG_TAG, "getCallWaiting"); 699 Message resp; 700 resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, onComplete); 701 702 try { 703 ImsUtInterface ut = mCT.getUtInterface(); 704 ut.queryCallWaiting(resp); 705 } catch (ImsException e) { 706 sendErrorResponse(onComplete, e); 707 } 708 } 709 710 @Override 711 public void setCallWaiting(boolean enable, Message onComplete) { 712 if (DBG) Rlog.d(LOG_TAG, "setCallWaiting enable=" + enable); 713 Message resp; 714 resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, onComplete); 715 716 try { 717 ImsUtInterface ut = mCT.getUtInterface(); 718 ut.updateCallWaiting(enable, resp); 719 } catch (ImsException e) { 720 sendErrorResponse(onComplete, e); 721 } 722 } 723 724 private int getCBTypeFromFacility(String facility) { 725 if (CB_FACILITY_BAOC.equals(facility)) { 726 return ImsUtInterface.CB_BAOC; 727 } else if (CB_FACILITY_BAOIC.equals(facility)) { 728 return ImsUtInterface.CB_BOIC; 729 } else if (CB_FACILITY_BAOICxH.equals(facility)) { 730 return ImsUtInterface.CB_BOIC_EXHC; 731 } else if (CB_FACILITY_BAIC.equals(facility)) { 732 return ImsUtInterface.CB_BAIC; 733 } else if (CB_FACILITY_BAICr.equals(facility)) { 734 return ImsUtInterface.CB_BIC_WR; 735 } else if (CB_FACILITY_BA_ALL.equals(facility)) { 736 return ImsUtInterface.CB_BA_ALL; 737 } else if (CB_FACILITY_BA_MO.equals(facility)) { 738 return ImsUtInterface.CB_BA_MO; 739 } else if (CB_FACILITY_BA_MT.equals(facility)) { 740 return ImsUtInterface.CB_BA_MT; 741 } 742 743 return 0; 744 } 745 746 /* package */ 747 void getCallBarring(String facility, Message onComplete) { 748 if (DBG) Rlog.d(LOG_TAG, "getCallBarring facility=" + facility); 749 Message resp; 750 resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, onComplete); 751 752 try { 753 ImsUtInterface ut = mCT.getUtInterface(); 754 ut.queryCallBarring(getCBTypeFromFacility(facility), resp); 755 } catch (ImsException e) { 756 sendErrorResponse(onComplete, e); 757 } 758 } 759 760 /* package */ 761 void setCallBarring(String facility, boolean lockState, String password, Message onComplete) { 762 if (DBG) Rlog.d(LOG_TAG, "setCallBarring facility=" + facility 763 + ", lockState=" + lockState); 764 Message resp; 765 resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, onComplete); 766 767 try { 768 ImsUtInterface ut = mCT.getUtInterface(); 769 // password is not required with Ut interface 770 ut.updateCallBarring(getCBTypeFromFacility(facility), lockState, resp, null); 771 } catch (ImsException e) { 772 sendErrorResponse(onComplete, e); 773 } 774 } 775 776 @Override 777 public void sendUssdResponse(String ussdMessge) { 778 Rlog.d(LOG_TAG, "sendUssdResponse"); 779 ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessge, this); 780 mPendingMMIs.add(mmi); 781 mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); 782 mmi.sendUssd(ussdMessge); 783 } 784 785 /* package */ 786 void sendUSSD (String ussdString, Message response) { 787 mCT.sendUSSD(ussdString, response); 788 } 789 790 /* package */ 791 void cancelUSSD() { 792 mCT.cancelUSSD(); 793 } 794 795 /* package */ 796 void sendErrorResponse(Message onComplete) { 797 Rlog.d(LOG_TAG, "sendErrorResponse"); 798 if (onComplete != null) { 799 AsyncResult.forMessage(onComplete, null, 800 new CommandException(CommandException.Error.GENERIC_FAILURE)); 801 onComplete.sendToTarget(); 802 } 803 } 804 805 /* package */ 806 void sendErrorResponse(Message onComplete, Throwable e) { 807 Rlog.d(LOG_TAG, "sendErrorResponse"); 808 if (onComplete != null) { 809 AsyncResult.forMessage(onComplete, null, getCommandException(e)); 810 onComplete.sendToTarget(); 811 } 812 } 813 814 /* package */ 815 void sendErrorResponse(Message onComplete, ImsReasonInfo reasonInfo) { 816 Rlog.d(LOG_TAG, "sendErrorResponse reasonCode=" + reasonInfo.getCode()); 817 if (onComplete != null) { 818 AsyncResult.forMessage(onComplete, null, getCommandException(reasonInfo.getCode())); 819 onComplete.sendToTarget(); 820 } 821 } 822 823 /* package */ 824 CommandException getCommandException(int code) { 825 Rlog.d(LOG_TAG, "getCommandException code=" + code); 826 CommandException.Error error = CommandException.Error.GENERIC_FAILURE; 827 828 switch(code) { 829 case ImsReasonInfo.CODE_UT_NOT_SUPPORTED: 830 error = CommandException.Error.REQUEST_NOT_SUPPORTED; 831 break; 832 case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH: 833 error = CommandException.Error.PASSWORD_INCORRECT; 834 break; 835 default: 836 break; 837 } 838 839 return new CommandException(error); 840 } 841 842 /* package */ 843 CommandException getCommandException(Throwable e) { 844 CommandException ex = null; 845 846 if (e instanceof ImsException) { 847 ex = getCommandException(((ImsException)e).getCode()); 848 } else { 849 Rlog.d(LOG_TAG, "getCommandException generic failure"); 850 ex = new CommandException(CommandException.Error.GENERIC_FAILURE); 851 } 852 return ex; 853 } 854 855 private void 856 onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) { 857 Rlog.d(LOG_TAG, "onNetworkInitiatedUssd"); 858 mMmiCompleteRegistrants.notifyRegistrants( 859 new AsyncResult(null, mmi, null)); 860 } 861 862 /* package */ 863 void onIncomingUSSD (int ussdMode, String ussdMessage) { 864 if (DBG) Rlog.d(LOG_TAG, "onIncomingUSSD ussdMode=" + ussdMode); 865 866 boolean isUssdError; 867 boolean isUssdRequest; 868 869 isUssdRequest 870 = (ussdMode == CommandsInterface.USSD_MODE_REQUEST); 871 872 isUssdError 873 = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY 874 && ussdMode != CommandsInterface.USSD_MODE_REQUEST); 875 876 ImsPhoneMmiCode found = null; 877 for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) { 878 if(mPendingMMIs.get(i).isPendingUSSD()) { 879 found = mPendingMMIs.get(i); 880 break; 881 } 882 } 883 884 if (found != null) { 885 // Complete pending USSD 886 if (isUssdError) { 887 found.onUssdFinishedError(); 888 } else { 889 found.onUssdFinished(ussdMessage, isUssdRequest); 890 } 891 } else { // pending USSD not found 892 // The network may initiate its own USSD request 893 894 // ignore everything that isnt a Notify or a Request 895 // also, discard if there is no message to present 896 if (!isUssdError && ussdMessage != null) { 897 ImsPhoneMmiCode mmi; 898 mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage, 899 isUssdRequest, 900 ImsPhone.this); 901 onNetworkInitiatedUssd(mmi); 902 } 903 } 904 } 905 906 /** 907 * Removes the given MMI from the pending list and notifies 908 * registrants that it is complete. 909 * @param mmi MMI that is done 910 */ 911 /*package*/ void 912 onMMIDone(ImsPhoneMmiCode mmi) { 913 /* Only notify complete if it's on the pending list. 914 * Otherwise, it's already been handled (eg, previously canceled). 915 * The exception is cancellation of an incoming USSD-REQUEST, which is 916 * not on the list. 917 */ 918 if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest()) { 919 mMmiCompleteRegistrants.notifyRegistrants( 920 new AsyncResult(null, mmi, null)); 921 } 922 } 923 924 public ArrayList<Connection> getHandoverConnection() { 925 ArrayList<Connection> connList = new ArrayList<Connection>(); 926 // Add all foreground call connections 927 connList.addAll(getForegroundCall().mConnections); 928 // Add all background call connections 929 connList.addAll(getBackgroundCall().mConnections); 930 // Add all background call connections 931 connList.addAll(getRingingCall().mConnections); 932 if (connList.size() > 0) { 933 return connList; 934 } else { 935 return null; 936 } 937 } 938 939 public void notifySrvccState(Call.SrvccState state) { 940 mCT.notifySrvccState(state); 941 } 942 943 /* package */ void 944 initiateSilentRedial() { 945 String result = mLastDialString; 946 AsyncResult ar = new AsyncResult(null, result, null); 947 if (ar != null) { 948 mSilentRedialRegistrants.notifyRegistrants(ar); 949 } 950 } 951 952 public void registerForSilentRedial(Handler h, int what, Object obj) { 953 mSilentRedialRegistrants.addUnique(h, what, obj); 954 } 955 956 public void unregisterForSilentRedial(Handler h) { 957 mSilentRedialRegistrants.remove(h); 958 } 959 960 @Override 961 public int getSubId() { 962 return mDefaultPhone.getSubId(); 963 } 964 965 @Override 966 public int getPhoneId() { 967 return mDefaultPhone.getPhoneId(); 968 } 969 970 private IccRecords getIccRecords() { 971 return mDefaultPhone.mIccRecords.get(); 972 } 973 974 private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) { 975 CallForwardInfo cfInfo = new CallForwardInfo(); 976 cfInfo.status = info.mStatus; 977 cfInfo.reason = getCFReasonFromCondition(info.mCondition); 978 cfInfo.serviceClass = SERVICE_CLASS_VOICE; 979 cfInfo.toa = info.mToA; 980 cfInfo.number = info.mNumber; 981 cfInfo.timeSeconds = info.mTimeSeconds; 982 return cfInfo; 983 } 984 985 private CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) { 986 CallForwardInfo[] cfInfos = null; 987 988 if (infos != null && infos.length != 0) { 989 cfInfos = new CallForwardInfo[infos.length]; 990 } 991 992 IccRecords r = getIccRecords(); 993 if (infos == null || infos.length == 0) { 994 if (r != null) { 995 // Assume the default is not active 996 // Set unconditional CFF in SIM to false 997 r.setVoiceCallForwardingFlag(1, false, null); 998 } 999 } else { 1000 for (int i = 0, s = infos.length; i < s; i++) { 1001 if (infos[i].mCondition == ImsUtInterface.CDIV_CF_UNCONDITIONAL) { 1002 if (r != null) { 1003 r.setVoiceCallForwardingFlag(1, (infos[i].mStatus == 1), 1004 infos[i].mNumber); 1005 } 1006 } 1007 cfInfos[i] = getCallForwardInfo(infos[i]); 1008 } 1009 } 1010 1011 return cfInfos; 1012 } 1013 1014 private int[] handleCbQueryResult(ImsSsInfo[] infos) { 1015 int[] cbInfos = new int[1]; 1016 cbInfos[0] = SERVICE_CLASS_NONE; 1017 1018 if (infos[0].mStatus == 1) { 1019 cbInfos[0] = SERVICE_CLASS_VOICE; 1020 } 1021 1022 return cbInfos; 1023 } 1024 1025 private int[] handleCwQueryResult(ImsSsInfo[] infos) { 1026 int[] cwInfos = new int[2]; 1027 cwInfos[0] = 0; 1028 1029 if (infos[0].mStatus == 1) { 1030 cwInfos[0] = 1; 1031 cwInfos[1] = SERVICE_CLASS_VOICE; 1032 } 1033 1034 return cwInfos; 1035 } 1036 1037 private void 1038 sendResponse(Message onComplete, Object result, Throwable e) { 1039 if (onComplete != null) { 1040 CommandException ex = null; 1041 ImsException imsEx = null; 1042 if (e != null) { 1043 if (e instanceof ImsException) { 1044 imsEx = (ImsException) e; 1045 AsyncResult.forMessage(onComplete, result, imsEx); 1046 } else { 1047 ex = getCommandException(e); 1048 AsyncResult.forMessage(onComplete, result, ex); 1049 } 1050 } else { 1051 AsyncResult.forMessage(onComplete, result, null); 1052 } 1053 onComplete.sendToTarget(); 1054 } 1055 } 1056 1057 @Override 1058 public void handleMessage (Message msg) { 1059 AsyncResult ar = (AsyncResult) msg.obj; 1060 Message onComplete; 1061 1062 if (DBG) Rlog.d(LOG_TAG, "handleMessage what=" + msg.what); 1063 switch (msg.what) { 1064 case EVENT_SET_CALL_FORWARD_DONE: 1065 IccRecords r = getIccRecords(); 1066 Cf cf = (Cf) ar.userObj; 1067 if (cf.mIsCfu && ar.exception == null && r != null) { 1068 r.setVoiceCallForwardingFlag(1, msg.arg1 == 1, cf.mSetCfNumber); 1069 } 1070 sendResponse(cf.mOnComplete, null, ar.exception); 1071 break; 1072 1073 case EVENT_GET_CALL_FORWARD_DONE: 1074 CallForwardInfo[] cfInfos = null; 1075 if (ar.exception == null) { 1076 cfInfos = handleCfQueryResult((ImsCallForwardInfo[])ar.result); 1077 } 1078 sendResponse((Message) ar.userObj, cfInfos, ar.exception); 1079 break; 1080 1081 case EVENT_GET_CALL_BARRING_DONE: 1082 case EVENT_GET_CALL_WAITING_DONE: 1083 int[] ssInfos = null; 1084 if (ar.exception == null) { 1085 if (msg.what == EVENT_GET_CALL_BARRING_DONE) { 1086 ssInfos = handleCbQueryResult((ImsSsInfo[])ar.result); 1087 } else if (msg.what == EVENT_GET_CALL_WAITING_DONE) { 1088 ssInfos = handleCwQueryResult((ImsSsInfo[])ar.result); 1089 } 1090 } 1091 sendResponse((Message) ar.userObj, ssInfos, ar.exception); 1092 break; 1093 1094 case EVENT_SET_CALL_BARRING_DONE: 1095 case EVENT_SET_CALL_WAITING_DONE: 1096 sendResponse((Message) ar.userObj, null, ar.exception); 1097 break; 1098 1099 default: 1100 super.handleMessage(msg); 1101 break; 1102 } 1103 } 1104 1105 /** 1106 * Listen to the IMS ECBM state change 1107 */ 1108 ImsEcbmStateListener mImsEcbmStateListener = 1109 new ImsEcbmStateListener() { 1110 @Override 1111 public void onECBMEntered() { 1112 if (DBG) Rlog.d(LOG_TAG, "onECBMEntered"); 1113 handleEnterEmergencyCallbackMode(); 1114 } 1115 1116 @Override 1117 public void onECBMExited() { 1118 if (DBG) Rlog.d(LOG_TAG, "onECBMExited"); 1119 handleExitEmergencyCallbackMode(); 1120 } 1121 }; 1122 1123 public boolean isInEmergencyCall() { 1124 return mCT.isInEmergencyCall(); 1125 } 1126 1127 public boolean isInEcm() { 1128 return mIsPhoneInEcmState; 1129 } 1130 1131 void sendEmergencyCallbackModeChange() { 1132 // Send an Intent 1133 Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); 1134 intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, mIsPhoneInEcmState); 1135 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId()); 1136 ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL); 1137 if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange"); 1138 } 1139 1140 @Override 1141 public void exitEmergencyCallbackMode() { 1142 if (mWakeLock.isHeld()) { 1143 mWakeLock.release(); 1144 } 1145 if (DBG) Rlog.d(LOG_TAG, "exitEmergencyCallbackMode()"); 1146 1147 // Send a message which will invoke handleExitEmergencyCallbackMode 1148 ImsEcbm ecbm; 1149 try { 1150 ecbm = mCT.getEcbmInterface(); 1151 ecbm.exitEmergencyCallbackMode(); 1152 } catch (ImsException e) { 1153 e.printStackTrace(); 1154 } 1155 } 1156 1157 private void handleEnterEmergencyCallbackMode() { 1158 if (DBG) { 1159 Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= " 1160 + mIsPhoneInEcmState); 1161 } 1162 // if phone is not in Ecm mode, and it's changed to Ecm mode 1163 if (mIsPhoneInEcmState == false) { 1164 mIsPhoneInEcmState = true; 1165 // notify change 1166 sendEmergencyCallbackModeChange(); 1167 setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "true"); 1168 1169 // Post this runnable so we will automatically exit 1170 // if no one invokes exitEmergencyCallbackMode() directly. 1171 long delayInMillis = SystemProperties.getLong( 1172 TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE); 1173 postDelayed(mExitEcmRunnable, delayInMillis); 1174 // We don't want to go to sleep while in Ecm 1175 mWakeLock.acquire(); 1176 } 1177 } 1178 1179 private void handleExitEmergencyCallbackMode() { 1180 if (DBG) { 1181 Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode: mIsPhoneInEcmState = " 1182 + mIsPhoneInEcmState); 1183 } 1184 // Remove pending exit Ecm runnable, if any 1185 removeCallbacks(mExitEcmRunnable); 1186 1187 if (mEcmExitRespRegistrant != null) { 1188 mEcmExitRespRegistrant.notifyResult(Boolean.TRUE); 1189 } 1190 if (mIsPhoneInEcmState) { 1191 mIsPhoneInEcmState = false; 1192 setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "false"); 1193 } 1194 // send an Intent 1195 sendEmergencyCallbackModeChange(); 1196 } 1197 1198 /** 1199 * Handle to cancel or restart Ecm timer in emergency call back mode if action is 1200 * CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled; otherwise, restart 1201 * Ecm timer and notify apps the timer is restarted. 1202 */ 1203 void handleTimerInEmergencyCallbackMode(int action) { 1204 switch (action) { 1205 case CANCEL_ECM_TIMER: 1206 removeCallbacks(mExitEcmRunnable); 1207 if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) { 1208 ((GSMPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.TRUE); 1209 } else { // Should be CDMA - also go here by default 1210 ((CDMAPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.TRUE); 1211 } 1212 break; 1213 case RESTART_ECM_TIMER: 1214 long delayInMillis = SystemProperties.getLong( 1215 TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE); 1216 postDelayed(mExitEcmRunnable, delayInMillis); 1217 if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) { 1218 ((GSMPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.FALSE); 1219 } else { // Should be CDMA - also go here by default 1220 ((CDMAPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.FALSE); 1221 } 1222 break; 1223 default: 1224 Rlog.e(LOG_TAG, "handleTimerInEmergencyCallbackMode, unsupported action " + action); 1225 } 1226 } 1227 1228 public void setOnEcbModeExitResponse(Handler h, int what, Object obj) { 1229 mEcmExitRespRegistrant = new Registrant(h, what, obj); 1230 } 1231 1232 public void unsetOnEcbModeExitResponse(Handler h) { 1233 mEcmExitRespRegistrant.clear(); 1234 } 1235 1236 public boolean isVolteEnabled() { 1237 return mCT.isVolteEnabled(); 1238 } 1239 1240 public boolean isVtEnabled() { 1241 return mCT.isVtEnabled(); 1242 } 1243 1244 public Phone getDefaultPhone() { 1245 return mDefaultPhone; 1246 } 1247} 1248