ImsPhone.java revision 6e5c70b724b1900d3c689f254cc1fb04e080ac07
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.Activity; 20import android.app.ActivityManagerNative; 21import android.app.Notification; 22import android.app.NotificationManager; 23import android.app.PendingIntent; 24import android.content.BroadcastReceiver; 25import android.content.Context; 26import android.content.Intent; 27import android.os.AsyncResult; 28import android.os.Bundle; 29import android.os.Handler; 30import android.os.Message; 31import android.os.PowerManager; 32import android.os.Registrant; 33import android.os.RegistrantList; 34import android.os.PowerManager.WakeLock; 35import android.os.SystemProperties; 36import android.os.UserHandle; 37 38import android.telephony.PhoneNumberUtils; 39import android.telephony.ServiceState; 40import android.telephony.Rlog; 41import android.telephony.SubscriptionManager; 42import android.text.TextUtils; 43 44import com.android.ims.ImsCallForwardInfo; 45import com.android.ims.ImsCallProfile; 46import com.android.ims.ImsEcbm; 47import com.android.ims.ImsEcbmStateListener; 48import com.android.ims.ImsException; 49import com.android.ims.ImsManager; 50import com.android.ims.ImsReasonInfo; 51import com.android.ims.ImsSsInfo; 52import com.android.ims.ImsUtInterface; 53 54import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC; 55import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC; 56import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH; 57import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC; 58import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr; 59import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL; 60import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO; 61import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT; 62 63import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE; 64import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE; 65import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE; 66import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION; 67import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL; 68import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL; 69import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY; 70import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE; 71import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY; 72import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL; 73import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE; 74import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE; 75 76import com.android.internal.telephony.Call; 77import com.android.internal.telephony.CallForwardInfo; 78import com.android.internal.telephony.CallStateException; 79import com.android.internal.telephony.CallTracker; 80import com.android.internal.telephony.CommandException; 81import com.android.internal.telephony.CommandsInterface; 82import com.android.internal.telephony.Connection; 83import com.android.internal.telephony.GsmCdmaPhone; 84import com.android.internal.telephony.Phone; 85import com.android.internal.telephony.PhoneConstants; 86import com.android.internal.telephony.PhoneNotifier; 87import com.android.internal.telephony.TelephonyIntents; 88import com.android.internal.telephony.TelephonyProperties; 89import com.android.internal.telephony.UUSInfo; 90import com.android.internal.telephony.gsm.SuppServiceNotification; 91import com.android.internal.telephony.uicc.IccRecords; 92 93import java.io.FileDescriptor; 94import java.io.PrintWriter; 95import java.util.ArrayList; 96import java.util.List; 97 98/** 99 * {@hide} 100 */ 101public class ImsPhone extends ImsPhoneBase { 102 private static final String LOG_TAG = "ImsPhone"; 103 private static final boolean DBG = true; 104 private static final boolean VDBG = false; // STOPSHIP if true 105 106 protected static final int EVENT_SET_CALL_BARRING_DONE = EVENT_LAST + 1; 107 protected static final int EVENT_GET_CALL_BARRING_DONE = EVENT_LAST + 2; 108 protected static final int EVENT_SET_CALL_WAITING_DONE = EVENT_LAST + 3; 109 protected static final int EVENT_GET_CALL_WAITING_DONE = EVENT_LAST + 4; 110 protected static final int EVENT_SET_CLIR_DONE = EVENT_LAST + 5; 111 protected static final int EVENT_GET_CLIR_DONE = EVENT_LAST + 6; 112 protected static final int EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED = EVENT_LAST + 7; 113 114 static final int RESTART_ECM_TIMER = 0; // restart Ecm timer 115 static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer 116 117 // Default Emergency Callback Mode exit timer 118 private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000; 119 120 // Instance Variables 121 Phone mDefaultPhone; 122 ImsPhoneCallTracker mCT; 123 ArrayList <ImsPhoneMmiCode> mPendingMMIs = new ArrayList<ImsPhoneMmiCode>(); 124 125 Registrant mPostDialHandler; 126 ServiceState mSS = new ServiceState(); 127 128 // To redial silently through GSM or CDMA when dialing through IMS fails 129 private String mLastDialString; 130 131 WakeLock mWakeLock; 132 protected boolean mIsPhoneInEcmState; 133 134 // mEcmExitRespRegistrant is informed after the phone has been exited the emergency 135 // callback mode keep track of if phone is in emergency callback mode 136 private Registrant mEcmExitRespRegistrant; 137 138 private final RegistrantList mSilentRedialRegistrants = new RegistrantList(); 139 140 private boolean mImsRegistered = false; 141 142 // List of Registrants to send supplementary service notifications to. 143 private RegistrantList mSsnRegistrants = new RegistrantList(); 144 145 // A runnable which is used to automatically exit from Ecm after a period of time. 146 private Runnable mExitEcmRunnable = new Runnable() { 147 @Override 148 public void run() { 149 exitEmergencyCallbackMode(); 150 } 151 }; 152 153 // Create Cf (Call forward) so that dialling number & 154 // mIsCfu (true if reason is call forward unconditional) 155 // mOnComplete (Message object passed by client) can be packed & 156 // given as a single Cf object as user data to UtInterface. 157 private static class Cf { 158 final String mSetCfNumber; 159 final Message mOnComplete; 160 final boolean mIsCfu; 161 162 Cf(String cfNumber, boolean isCfu, Message onComplete) { 163 mSetCfNumber = cfNumber; 164 mIsCfu = isCfu; 165 mOnComplete = onComplete; 166 } 167 } 168 169 // Constructors 170 171 ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone) { 172 super("ImsPhone", context, notifier); 173 174 mDefaultPhone = (Phone) defaultPhone; 175 mCT = new ImsPhoneCallTracker(this); 176 mSS.setStateOff(); 177 178 mPhoneId = mDefaultPhone.getPhoneId(); 179 180 // This is needed to handle phone process crashes 181 // Same property is used for both CDMA & IMS phone. 182 mIsPhoneInEcmState = SystemProperties.getBoolean( 183 TelephonyProperties.PROPERTY_INECM_MODE, false); 184 185 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 186 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); 187 mWakeLock.setReferenceCounted(false); 188 189 if (mDefaultPhone.getServiceStateTracker() != null) { 190 mDefaultPhone.getServiceStateTracker() 191 .registerForDataRegStateOrRatChanged(this, 192 EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED, null); 193 } 194 updateDataServiceState(); 195 } 196 197 @Override 198 public void updateParentPhone(Phone parentPhone) { 199 synchronized (Phone.lockForRadioTechnologyChange) { 200 mDefaultPhone = parentPhone; 201 mPhoneId = mDefaultPhone.getPhoneId(); 202 if (mDefaultPhone.getServiceStateTracker() != null) { 203 mDefaultPhone.getServiceStateTracker() 204 .registerForDataRegStateOrRatChanged(this, 205 EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED, null); 206 } 207 updateDataServiceState(); 208 209 // When the parent phone is updated, we need to notify listeners of the cached video 210 // capability. 211 Rlog.d(LOG_TAG, "updateParentPhone - Notify video capability changed " + mIsVideoCapable); 212 notifyForVideoCapabilityChanged(mIsVideoCapable); 213 } 214 } 215 216 @Override 217 public void dispose() { 218 Rlog.d(LOG_TAG, "dispose"); 219 // Nothing to dispose in Phone 220 //super.dispose(); 221 mPendingMMIs.clear(); 222 mCT.dispose(); 223 224 //Force all referenced classes to unregister their former registered events 225 if (mDefaultPhone != null && mDefaultPhone.getServiceStateTracker() != null) { 226 mDefaultPhone.getServiceStateTracker(). 227 unregisterForDataRegStateOrRatChanged(this); 228 } 229 } 230 231 @Override 232 public ServiceState 233 getServiceState() { 234 return mSS; 235 } 236 237 /* package */ void setServiceState(int state) { 238 mSS.setVoiceRegState(state); 239 updateDataServiceState(); 240 } 241 242 @Override 243 public CallTracker getCallTracker() { 244 return mCT; 245 } 246 247 @Override 248 public List<? extends ImsPhoneMmiCode> 249 getPendingMmiCodes() { 250 return mPendingMMIs; 251 } 252 253 254 @Override 255 public void 256 acceptCall(int videoState) throws CallStateException { 257 mCT.acceptCall(videoState); 258 } 259 260 @Override 261 public void 262 rejectCall() throws CallStateException { 263 mCT.rejectCall(); 264 } 265 266 @Override 267 public void 268 switchHoldingAndActive() throws CallStateException { 269 mCT.switchWaitingOrHoldingAndActive(); 270 } 271 272 @Override 273 public boolean canConference() { 274 return mCT.canConference(); 275 } 276 277 public boolean canDial() { 278 return mCT.canDial(); 279 } 280 281 @Override 282 public void conference() { 283 mCT.conference(); 284 } 285 286 @Override 287 public void clearDisconnected() { 288 mCT.clearDisconnected(); 289 } 290 291 @Override 292 public boolean canTransfer() { 293 return mCT.canTransfer(); 294 } 295 296 @Override 297 public void explicitCallTransfer() { 298 mCT.explicitCallTransfer(); 299 } 300 301 @Override 302 public ImsPhoneCall 303 getForegroundCall() { 304 return mCT.mForegroundCall; 305 } 306 307 @Override 308 public ImsPhoneCall 309 getBackgroundCall() { 310 return mCT.mBackgroundCall; 311 } 312 313 @Override 314 public ImsPhoneCall 315 getRingingCall() { 316 return mCT.mRingingCall; 317 } 318 319 private boolean handleCallDeflectionIncallSupplementaryService( 320 String dialString) { 321 if (dialString.length() > 1) { 322 return false; 323 } 324 325 if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) { 326 if (DBG) Rlog.d(LOG_TAG, "MmiCode 0: rejectCall"); 327 try { 328 mCT.rejectCall(); 329 } catch (CallStateException e) { 330 if (DBG) Rlog.d(LOG_TAG, "reject failed", e); 331 notifySuppServiceFailed(Phone.SuppService.REJECT); 332 } 333 } else if (getBackgroundCall().getState() != ImsPhoneCall.State.IDLE) { 334 if (DBG) Rlog.d(LOG_TAG, "MmiCode 0: hangupWaitingOrBackground"); 335 try { 336 mCT.hangup(getBackgroundCall()); 337 } catch (CallStateException e) { 338 if (DBG) Rlog.d(LOG_TAG, "hangup failed", e); 339 } 340 } 341 342 return true; 343 } 344 345 346 private boolean handleCallWaitingIncallSupplementaryService( 347 String dialString) { 348 int len = dialString.length(); 349 350 if (len > 2) { 351 return false; 352 } 353 354 ImsPhoneCall call = getForegroundCall(); 355 356 try { 357 if (len > 1) { 358 if (DBG) Rlog.d(LOG_TAG, "not support 1X SEND"); 359 notifySuppServiceFailed(Phone.SuppService.HANGUP); 360 } else { 361 if (call.getState() != ImsPhoneCall.State.IDLE) { 362 if (DBG) Rlog.d(LOG_TAG, "MmiCode 1: hangup foreground"); 363 mCT.hangup(call); 364 } else { 365 if (DBG) Rlog.d(LOG_TAG, "MmiCode 1: switchWaitingOrHoldingAndActive"); 366 mCT.switchWaitingOrHoldingAndActive(); 367 } 368 } 369 } catch (CallStateException e) { 370 if (DBG) Rlog.d(LOG_TAG, "hangup failed", e); 371 notifySuppServiceFailed(Phone.SuppService.HANGUP); 372 } 373 374 return true; 375 } 376 377 private boolean handleCallHoldIncallSupplementaryService(String dialString) { 378 int len = dialString.length(); 379 380 if (len > 2) { 381 return false; 382 } 383 384 ImsPhoneCall call = getForegroundCall(); 385 386 if (len > 1) { 387 if (DBG) Rlog.d(LOG_TAG, "separate not supported"); 388 notifySuppServiceFailed(Phone.SuppService.SEPARATE); 389 } else { 390 try { 391 if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) { 392 if (DBG) Rlog.d(LOG_TAG, "MmiCode 2: accept ringing call"); 393 mCT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE); 394 } else { 395 if (DBG) Rlog.d(LOG_TAG, "MmiCode 2: switchWaitingOrHoldingAndActive"); 396 mCT.switchWaitingOrHoldingAndActive(); 397 } 398 } catch (CallStateException e) { 399 if (DBG) Rlog.d(LOG_TAG, "switch failed", e); 400 notifySuppServiceFailed(Phone.SuppService.SWITCH); 401 } 402 } 403 404 return true; 405 } 406 407 private boolean handleMultipartyIncallSupplementaryService( 408 String dialString) { 409 if (dialString.length() > 1) { 410 return false; 411 } 412 413 if (DBG) Rlog.d(LOG_TAG, "MmiCode 3: merge calls"); 414 conference(); 415 return true; 416 } 417 418 private boolean handleEctIncallSupplementaryService(String dialString) { 419 420 int len = dialString.length(); 421 422 if (len != 1) { 423 return false; 424 } 425 426 if (DBG) Rlog.d(LOG_TAG, "MmiCode 4: not support explicit call transfer"); 427 notifySuppServiceFailed(Phone.SuppService.TRANSFER); 428 return true; 429 } 430 431 private boolean handleCcbsIncallSupplementaryService(String dialString) { 432 if (dialString.length() > 1) { 433 return false; 434 } 435 436 Rlog.i(LOG_TAG, "MmiCode 5: CCBS not supported!"); 437 // Treat it as an "unknown" service. 438 notifySuppServiceFailed(Phone.SuppService.UNKNOWN); 439 return true; 440 } 441 442 public void notifySuppSvcNotification(SuppServiceNotification suppSvc) { 443 Rlog.d(LOG_TAG, "notifySuppSvcNotification: suppSvc = " + suppSvc); 444 445 AsyncResult ar = new AsyncResult(null, suppSvc, null); 446 mSsnRegistrants.notifyRegistrants(ar); 447 } 448 449 @Override 450 public boolean handleInCallMmiCommands(String dialString) { 451 if (!isInCall()) { 452 return false; 453 } 454 455 if (TextUtils.isEmpty(dialString)) { 456 return false; 457 } 458 459 boolean result = false; 460 char ch = dialString.charAt(0); 461 switch (ch) { 462 case '0': 463 result = handleCallDeflectionIncallSupplementaryService( 464 dialString); 465 break; 466 case '1': 467 result = handleCallWaitingIncallSupplementaryService( 468 dialString); 469 break; 470 case '2': 471 result = handleCallHoldIncallSupplementaryService(dialString); 472 break; 473 case '3': 474 result = handleMultipartyIncallSupplementaryService(dialString); 475 break; 476 case '4': 477 result = handleEctIncallSupplementaryService(dialString); 478 break; 479 case '5': 480 result = handleCcbsIncallSupplementaryService(dialString); 481 break; 482 default: 483 break; 484 } 485 486 return result; 487 } 488 489 boolean isInCall() { 490 ImsPhoneCall.State foregroundCallState = getForegroundCall().getState(); 491 ImsPhoneCall.State backgroundCallState = getBackgroundCall().getState(); 492 ImsPhoneCall.State ringingCallState = getRingingCall().getState(); 493 494 return (foregroundCallState.isAlive() || 495 backgroundCallState.isAlive() || 496 ringingCallState.isAlive()); 497 } 498 499 void notifyNewRingingConnection(Connection c) { 500 mDefaultPhone.notifyNewRingingConnectionP(c); 501 } 502 503 void notifyUnknownConnection(Connection c) { 504 mDefaultPhone.notifyUnknownConnectionP(c); 505 } 506 507 public void notifyForVideoCapabilityChanged(boolean isVideoCapable) { 508 mIsVideoCapable = isVideoCapable; 509 mDefaultPhone.notifyForVideoCapabilityChanged(isVideoCapable); 510 } 511 512 @Override 513 public Connection 514 dial(String dialString, int videoState) throws CallStateException { 515 return dialInternal(dialString, videoState, null); 516 } 517 518 @Override 519 public Connection 520 dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras) 521 throws CallStateException { 522 // ignore UUSInfo 523 return dialInternal (dialString, videoState, intentExtras); 524 } 525 526 protected Connection dialInternal(String dialString, int videoState, Bundle intentExtras) 527 throws CallStateException { 528 // Need to make sure dialString gets parsed properly 529 String newDialString = PhoneNumberUtils.stripSeparators(dialString); 530 531 // handle in-call MMI first if applicable 532 if (handleInCallMmiCommands(newDialString)) { 533 return null; 534 } 535 536 if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { 537 return mCT.dial(dialString, videoState, intentExtras); 538 } 539 540 // Only look at the Network portion for mmi 541 String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString); 542 ImsPhoneMmiCode mmi = 543 ImsPhoneMmiCode.newFromDialString(networkPortion, this); 544 if (DBG) Rlog.d(LOG_TAG, 545 "dialing w/ mmi '" + mmi + "'..."); 546 547 if (mmi == null) { 548 return mCT.dial(dialString, videoState, intentExtras); 549 } else if (mmi.isTemporaryModeCLIR()) { 550 return mCT.dial(mmi.getDialingNumber(), mmi.getCLIRMode(), videoState, intentExtras); 551 } else if (!mmi.isSupportedOverImsPhone()) { 552 // If the mmi is not supported by IMS service, 553 // try to initiate dialing with default phone 554 throw new CallStateException(CS_FALLBACK); 555 } else { 556 mPendingMMIs.add(mmi); 557 mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); 558 mmi.processCode(); 559 560 return null; 561 } 562 } 563 564 @Override 565 public void 566 sendDtmf(char c) { 567 if (!PhoneNumberUtils.is12Key(c)) { 568 Rlog.e(LOG_TAG, 569 "sendDtmf called with invalid character '" + c + "'"); 570 } else { 571 if (mCT.mState == PhoneConstants.State.OFFHOOK) { 572 mCT.sendDtmf(c, null); 573 } 574 } 575 } 576 577 @Override 578 public void 579 startDtmf(char c) { 580 if (!(PhoneNumberUtils.is12Key(c) || (c >= 'A' && c <= 'D'))) { 581 Rlog.e(LOG_TAG, 582 "startDtmf called with invalid character '" + c + "'"); 583 } else { 584 mCT.startDtmf(c); 585 } 586 } 587 588 @Override 589 public void 590 stopDtmf() { 591 mCT.stopDtmf(); 592 } 593 594 @Override 595 public void setOnPostDialCharacter(Handler h, int what, Object obj) { 596 mPostDialHandler = new Registrant(h, what, obj); 597 } 598 599 /*package*/ void notifyIncomingRing() { 600 if (DBG) Rlog.d(LOG_TAG, "notifyIncomingRing"); 601 AsyncResult ar = new AsyncResult(null, null, null); 602 sendMessage(obtainMessage(EVENT_CALL_RING, ar)); 603 } 604 605 @Override 606 public void setMute(boolean muted) { 607 mCT.setMute(muted); 608 } 609 610 @Override 611 public void setUiTTYMode(int uiTtyMode, Message onComplete) { 612 mCT.setUiTTYMode(uiTtyMode, onComplete); 613 } 614 615 @Override 616 public boolean getMute() { 617 return mCT.getMute(); 618 } 619 620 @Override 621 public PhoneConstants.State getState() { 622 return mCT.mState; 623 } 624 625 private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) { 626 switch (commandInterfaceCFReason) { 627 case CF_REASON_UNCONDITIONAL: 628 case CF_REASON_BUSY: 629 case CF_REASON_NO_REPLY: 630 case CF_REASON_NOT_REACHABLE: 631 case CF_REASON_ALL: 632 case CF_REASON_ALL_CONDITIONAL: 633 return true; 634 default: 635 return false; 636 } 637 } 638 639 private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) { 640 switch (commandInterfaceCFAction) { 641 case CF_ACTION_DISABLE: 642 case CF_ACTION_ENABLE: 643 case CF_ACTION_REGISTRATION: 644 case CF_ACTION_ERASURE: 645 return true; 646 default: 647 return false; 648 } 649 } 650 651 private boolean isCfEnable(int action) { 652 return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION); 653 } 654 655 private int getConditionFromCFReason(int reason) { 656 switch(reason) { 657 case CF_REASON_UNCONDITIONAL: return ImsUtInterface.CDIV_CF_UNCONDITIONAL; 658 case CF_REASON_BUSY: return ImsUtInterface.CDIV_CF_BUSY; 659 case CF_REASON_NO_REPLY: return ImsUtInterface.CDIV_CF_NO_REPLY; 660 case CF_REASON_NOT_REACHABLE: return ImsUtInterface.CDIV_CF_NOT_REACHABLE; 661 case CF_REASON_ALL: return ImsUtInterface.CDIV_CF_ALL; 662 case CF_REASON_ALL_CONDITIONAL: return ImsUtInterface.CDIV_CF_ALL_CONDITIONAL; 663 default: 664 break; 665 } 666 667 return ImsUtInterface.INVALID; 668 } 669 670 private int getCFReasonFromCondition(int condition) { 671 switch(condition) { 672 case ImsUtInterface.CDIV_CF_UNCONDITIONAL: return CF_REASON_UNCONDITIONAL; 673 case ImsUtInterface.CDIV_CF_BUSY: return CF_REASON_BUSY; 674 case ImsUtInterface.CDIV_CF_NO_REPLY: return CF_REASON_NO_REPLY; 675 case ImsUtInterface.CDIV_CF_NOT_REACHABLE: return CF_REASON_NOT_REACHABLE; 676 case ImsUtInterface.CDIV_CF_ALL: return CF_REASON_ALL; 677 case ImsUtInterface.CDIV_CF_ALL_CONDITIONAL: return CF_REASON_ALL_CONDITIONAL; 678 default: 679 break; 680 } 681 682 return CF_REASON_NOT_REACHABLE; 683 } 684 685 private int getActionFromCFAction(int action) { 686 switch(action) { 687 case CF_ACTION_DISABLE: return ImsUtInterface.ACTION_DEACTIVATION; 688 case CF_ACTION_ENABLE: return ImsUtInterface.ACTION_ACTIVATION; 689 case CF_ACTION_ERASURE: return ImsUtInterface.ACTION_ERASURE; 690 case CF_ACTION_REGISTRATION: return ImsUtInterface.ACTION_REGISTRATION; 691 default: 692 break; 693 } 694 695 return ImsUtInterface.INVALID; 696 } 697 698 @Override 699 public void getOutgoingCallerIdDisplay(Message onComplete) { 700 if (DBG) Rlog.d(LOG_TAG, "getCLIR"); 701 Message resp; 702 resp = obtainMessage(EVENT_GET_CLIR_DONE, onComplete); 703 704 try { 705 ImsUtInterface ut = mCT.getUtInterface(); 706 ut.queryCLIR(resp); 707 } catch (ImsException e) { 708 sendErrorResponse(onComplete, e); 709 } 710 } 711 712 @Override 713 public void setOutgoingCallerIdDisplay(int clirMode, Message onComplete) { 714 if (DBG) Rlog.d(LOG_TAG, "setCLIR action= " + clirMode); 715 Message resp; 716 // Packing CLIR value in the message. This will be required for 717 // SharedPreference caching, if the message comes back as part of 718 // a success response. 719 resp = obtainMessage(EVENT_SET_CLIR_DONE, clirMode, 0, onComplete); 720 try { 721 ImsUtInterface ut = mCT.getUtInterface(); 722 ut.updateCLIR(clirMode, resp); 723 } catch (ImsException e) { 724 sendErrorResponse(onComplete, e); 725 } 726 } 727 728 @Override 729 public void getCallForwardingOption(int commandInterfaceCFReason, 730 Message onComplete) { 731 if (DBG) Rlog.d(LOG_TAG, "getCallForwardingOption reason=" + commandInterfaceCFReason); 732 if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) { 733 if (DBG) Rlog.d(LOG_TAG, "requesting call forwarding query."); 734 Message resp; 735 resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete); 736 737 try { 738 ImsUtInterface ut = mCT.getUtInterface(); 739 ut.queryCallForward(getConditionFromCFReason(commandInterfaceCFReason), null, resp); 740 } catch (ImsException e) { 741 sendErrorResponse(onComplete, e); 742 } 743 } else if (onComplete != null) { 744 sendErrorResponse(onComplete); 745 } 746 } 747 748 @Override 749 public void setCallForwardingOption(int commandInterfaceCFAction, 750 int commandInterfaceCFReason, 751 String dialingNumber, 752 int timerSeconds, 753 Message onComplete) { 754 setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason, dialingNumber, 755 CommandsInterface.SERVICE_CLASS_VOICE, timerSeconds, onComplete); 756 } 757 758 public void setCallForwardingOption(int commandInterfaceCFAction, 759 int commandInterfaceCFReason, 760 String dialingNumber, 761 int serviceClass, 762 int timerSeconds, 763 Message onComplete) { 764 if (DBG) Rlog.d(LOG_TAG, "setCallForwardingOption action=" + commandInterfaceCFAction 765 + ", reason=" + commandInterfaceCFReason + " serviceClass=" + serviceClass); 766 if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) && 767 (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) { 768 Message resp; 769 Cf cf = new Cf(dialingNumber, 770 (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL ? true : false), 771 onComplete); 772 resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE, 773 isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, cf); 774 775 try { 776 ImsUtInterface ut = mCT.getUtInterface(); 777 ut.updateCallForward(getActionFromCFAction(commandInterfaceCFAction), 778 getConditionFromCFReason(commandInterfaceCFReason), 779 dialingNumber, 780 serviceClass, 781 timerSeconds, 782 onComplete); 783 } catch (ImsException e) { 784 sendErrorResponse(onComplete, e); 785 } 786 } else if (onComplete != null) { 787 sendErrorResponse(onComplete); 788 } 789 } 790 791 @Override 792 public void getCallWaiting(Message onComplete) { 793 if (DBG) Rlog.d(LOG_TAG, "getCallWaiting"); 794 Message resp; 795 resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, onComplete); 796 797 try { 798 ImsUtInterface ut = mCT.getUtInterface(); 799 ut.queryCallWaiting(resp); 800 } catch (ImsException e) { 801 sendErrorResponse(onComplete, e); 802 } 803 } 804 805 @Override 806 public void setCallWaiting(boolean enable, Message onComplete) { 807 setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete); 808 } 809 810 public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) { 811 if (DBG) Rlog.d(LOG_TAG, "setCallWaiting enable=" + enable); 812 Message resp; 813 resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, onComplete); 814 815 try { 816 ImsUtInterface ut = mCT.getUtInterface(); 817 ut.updateCallWaiting(enable, serviceClass, resp); 818 } catch (ImsException e) { 819 sendErrorResponse(onComplete, e); 820 } 821 } 822 823 private int getCBTypeFromFacility(String facility) { 824 if (CB_FACILITY_BAOC.equals(facility)) { 825 return ImsUtInterface.CB_BAOC; 826 } else if (CB_FACILITY_BAOIC.equals(facility)) { 827 return ImsUtInterface.CB_BOIC; 828 } else if (CB_FACILITY_BAOICxH.equals(facility)) { 829 return ImsUtInterface.CB_BOIC_EXHC; 830 } else if (CB_FACILITY_BAIC.equals(facility)) { 831 return ImsUtInterface.CB_BAIC; 832 } else if (CB_FACILITY_BAICr.equals(facility)) { 833 return ImsUtInterface.CB_BIC_WR; 834 } else if (CB_FACILITY_BA_ALL.equals(facility)) { 835 return ImsUtInterface.CB_BA_ALL; 836 } else if (CB_FACILITY_BA_MO.equals(facility)) { 837 return ImsUtInterface.CB_BA_MO; 838 } else if (CB_FACILITY_BA_MT.equals(facility)) { 839 return ImsUtInterface.CB_BA_MT; 840 } 841 842 return 0; 843 } 844 845 /* package */ 846 void getCallBarring(String facility, Message onComplete) { 847 if (DBG) Rlog.d(LOG_TAG, "getCallBarring facility=" + facility); 848 Message resp; 849 resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, onComplete); 850 851 try { 852 ImsUtInterface ut = mCT.getUtInterface(); 853 ut.queryCallBarring(getCBTypeFromFacility(facility), resp); 854 } catch (ImsException e) { 855 sendErrorResponse(onComplete, e); 856 } 857 } 858 859 /* package */ 860 void setCallBarring(String facility, boolean lockState, String password, Message onComplete) { 861 if (DBG) Rlog.d(LOG_TAG, "setCallBarring facility=" + facility 862 + ", lockState=" + lockState); 863 Message resp; 864 resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, onComplete); 865 866 int action; 867 if (lockState) { 868 action = CommandsInterface.CF_ACTION_ENABLE; 869 } 870 else { 871 action = CommandsInterface.CF_ACTION_DISABLE; 872 } 873 874 try { 875 ImsUtInterface ut = mCT.getUtInterface(); 876 // password is not required with Ut interface 877 ut.updateCallBarring(getCBTypeFromFacility(facility), action, resp, null); 878 } catch (ImsException e) { 879 sendErrorResponse(onComplete, e); 880 } 881 } 882 883 @Override 884 public void sendUssdResponse(String ussdMessge) { 885 Rlog.d(LOG_TAG, "sendUssdResponse"); 886 ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessge, this); 887 mPendingMMIs.add(mmi); 888 mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); 889 mmi.sendUssd(ussdMessge); 890 } 891 892 /* package */ 893 void sendUSSD (String ussdString, Message response) { 894 mCT.sendUSSD(ussdString, response); 895 } 896 897 @Override 898 public void cancelUSSD() { 899 mCT.cancelUSSD(); 900 } 901 902 /* package */ 903 void sendErrorResponse(Message onComplete) { 904 Rlog.d(LOG_TAG, "sendErrorResponse"); 905 if (onComplete != null) { 906 AsyncResult.forMessage(onComplete, null, 907 new CommandException(CommandException.Error.GENERIC_FAILURE)); 908 onComplete.sendToTarget(); 909 } 910 } 911 912 /* package */ 913 void sendErrorResponse(Message onComplete, Throwable e) { 914 Rlog.d(LOG_TAG, "sendErrorResponse"); 915 if (onComplete != null) { 916 AsyncResult.forMessage(onComplete, null, getCommandException(e)); 917 onComplete.sendToTarget(); 918 } 919 } 920 921 /* package */ 922 void sendErrorResponse(Message onComplete, ImsReasonInfo reasonInfo) { 923 Rlog.d(LOG_TAG, "sendErrorResponse reasonCode=" + reasonInfo.getCode()); 924 if (onComplete != null) { 925 AsyncResult.forMessage(onComplete, null, getCommandException(reasonInfo.getCode())); 926 onComplete.sendToTarget(); 927 } 928 } 929 930 /* package */ 931 CommandException getCommandException(int code) { 932 return getCommandException(code, null); 933 } 934 935 CommandException getCommandException(int code, String errorString) { 936 Rlog.d(LOG_TAG, "getCommandException code= " + code 937 + ", errorString= " + errorString); 938 CommandException.Error error = CommandException.Error.GENERIC_FAILURE; 939 940 switch(code) { 941 case ImsReasonInfo.CODE_UT_NOT_SUPPORTED: 942 error = CommandException.Error.REQUEST_NOT_SUPPORTED; 943 break; 944 case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH: 945 error = CommandException.Error.PASSWORD_INCORRECT; 946 break; 947 case ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE: 948 error = CommandException.Error.RADIO_NOT_AVAILABLE; 949 default: 950 break; 951 } 952 953 return new CommandException(error, errorString); 954 } 955 956 /* package */ 957 CommandException getCommandException(Throwable e) { 958 CommandException ex = null; 959 960 if (e instanceof ImsException) { 961 ex = getCommandException(((ImsException)e).getCode(), e.getMessage()); 962 } else { 963 Rlog.d(LOG_TAG, "getCommandException generic failure"); 964 ex = new CommandException(CommandException.Error.GENERIC_FAILURE); 965 } 966 return ex; 967 } 968 969 private void 970 onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) { 971 Rlog.d(LOG_TAG, "onNetworkInitiatedUssd"); 972 mMmiCompleteRegistrants.notifyRegistrants( 973 new AsyncResult(null, mmi, null)); 974 } 975 976 /* package */ 977 void onIncomingUSSD (int ussdMode, String ussdMessage) { 978 if (DBG) Rlog.d(LOG_TAG, "onIncomingUSSD ussdMode=" + ussdMode); 979 980 boolean isUssdError; 981 boolean isUssdRequest; 982 983 isUssdRequest 984 = (ussdMode == CommandsInterface.USSD_MODE_REQUEST); 985 986 isUssdError 987 = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY 988 && ussdMode != CommandsInterface.USSD_MODE_REQUEST); 989 990 ImsPhoneMmiCode found = null; 991 for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) { 992 if(mPendingMMIs.get(i).isPendingUSSD()) { 993 found = mPendingMMIs.get(i); 994 break; 995 } 996 } 997 998 if (found != null) { 999 // Complete pending USSD 1000 if (isUssdError) { 1001 found.onUssdFinishedError(); 1002 } else { 1003 found.onUssdFinished(ussdMessage, isUssdRequest); 1004 } 1005 } else { // pending USSD not found 1006 // The network may initiate its own USSD request 1007 1008 // ignore everything that isnt a Notify or a Request 1009 // also, discard if there is no message to present 1010 if (!isUssdError && ussdMessage != null) { 1011 ImsPhoneMmiCode mmi; 1012 mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage, 1013 isUssdRequest, 1014 this); 1015 onNetworkInitiatedUssd(mmi); 1016 } 1017 } 1018 } 1019 1020 /** 1021 * Removes the given MMI from the pending list and notifies 1022 * registrants that it is complete. 1023 * @param mmi MMI that is done 1024 */ 1025 public void onMMIDone(ImsPhoneMmiCode mmi) { 1026 /* Only notify complete if it's on the pending list. 1027 * Otherwise, it's already been handled (eg, previously canceled). 1028 * The exception is cancellation of an incoming USSD-REQUEST, which is 1029 * not on the list. 1030 */ 1031 if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest()) { 1032 mMmiCompleteRegistrants.notifyRegistrants( 1033 new AsyncResult(null, mmi, null)); 1034 } 1035 } 1036 1037 @Override 1038 public ArrayList<Connection> getHandoverConnection() { 1039 ArrayList<Connection> connList = new ArrayList<Connection>(); 1040 // Add all foreground call connections 1041 connList.addAll(getForegroundCall().mConnections); 1042 // Add all background call connections 1043 connList.addAll(getBackgroundCall().mConnections); 1044 // Add all background call connections 1045 connList.addAll(getRingingCall().mConnections); 1046 if (connList.size() > 0) { 1047 return connList; 1048 } else { 1049 return null; 1050 } 1051 } 1052 1053 @Override 1054 public void notifySrvccState(Call.SrvccState state) { 1055 mCT.notifySrvccState(state); 1056 } 1057 1058 /* package */ void 1059 initiateSilentRedial() { 1060 String result = mLastDialString; 1061 AsyncResult ar = new AsyncResult(null, result, null); 1062 if (ar != null) { 1063 mSilentRedialRegistrants.notifyRegistrants(ar); 1064 } 1065 } 1066 1067 @Override 1068 public void registerForSilentRedial(Handler h, int what, Object obj) { 1069 mSilentRedialRegistrants.addUnique(h, what, obj); 1070 } 1071 1072 @Override 1073 public void unregisterForSilentRedial(Handler h) { 1074 mSilentRedialRegistrants.remove(h); 1075 } 1076 1077 @Override 1078 public void registerForSuppServiceNotification(Handler h, int what, Object obj) { 1079 mSsnRegistrants.addUnique(h, what, obj); 1080 } 1081 1082 @Override 1083 public void unregisterForSuppServiceNotification(Handler h) { 1084 mSsnRegistrants.remove(h); 1085 } 1086 1087 @Override 1088 public int getSubId() { 1089 return mDefaultPhone.getSubId(); 1090 } 1091 1092 @Override 1093 public int getPhoneId() { 1094 return mDefaultPhone.getPhoneId(); 1095 } 1096 1097 private IccRecords getIccRecords() { 1098 return mDefaultPhone.mIccRecords.get(); 1099 } 1100 1101 private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) { 1102 CallForwardInfo cfInfo = new CallForwardInfo(); 1103 cfInfo.status = info.mStatus; 1104 cfInfo.reason = getCFReasonFromCondition(info.mCondition); 1105 cfInfo.serviceClass = SERVICE_CLASS_VOICE; 1106 cfInfo.toa = info.mToA; 1107 cfInfo.number = info.mNumber; 1108 cfInfo.timeSeconds = info.mTimeSeconds; 1109 return cfInfo; 1110 } 1111 1112 private CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) { 1113 CallForwardInfo[] cfInfos = null; 1114 1115 if (infos != null && infos.length != 0) { 1116 cfInfos = new CallForwardInfo[infos.length]; 1117 } 1118 1119 IccRecords r = getIccRecords(); 1120 if (infos == null || infos.length == 0) { 1121 if (r != null) { 1122 // Assume the default is not active 1123 // Set unconditional CFF in SIM to false 1124 setVoiceCallForwardingFlag(r, 1, false, null); 1125 } 1126 } else { 1127 for (int i = 0, s = infos.length; i < s; i++) { 1128 if (infos[i].mCondition == ImsUtInterface.CDIV_CF_UNCONDITIONAL) { 1129 if (r != null) { 1130 setVoiceCallForwardingFlag(r, 1, (infos[i].mStatus == 1), 1131 infos[i].mNumber); 1132 } 1133 } 1134 cfInfos[i] = getCallForwardInfo(infos[i]); 1135 } 1136 } 1137 1138 return cfInfos; 1139 } 1140 1141 private int[] handleCbQueryResult(ImsSsInfo[] infos) { 1142 int[] cbInfos = new int[1]; 1143 cbInfos[0] = SERVICE_CLASS_NONE; 1144 1145 if (infos[0].mStatus == 1) { 1146 cbInfos[0] = SERVICE_CLASS_VOICE; 1147 } 1148 1149 return cbInfos; 1150 } 1151 1152 private int[] handleCwQueryResult(ImsSsInfo[] infos) { 1153 int[] cwInfos = new int[2]; 1154 cwInfos[0] = 0; 1155 1156 if (infos[0].mStatus == 1) { 1157 cwInfos[0] = 1; 1158 cwInfos[1] = SERVICE_CLASS_VOICE; 1159 } 1160 1161 return cwInfos; 1162 } 1163 1164 private void 1165 sendResponse(Message onComplete, Object result, Throwable e) { 1166 if (onComplete != null) { 1167 CommandException ex = null; 1168 if (e != null) { 1169 ex = getCommandException(e); 1170 } 1171 AsyncResult.forMessage(onComplete, result, ex); 1172 onComplete.sendToTarget(); 1173 } 1174 } 1175 1176 private void updateDataServiceState() { 1177 if (mSS != null && mDefaultPhone.getServiceStateTracker() != null 1178 && mDefaultPhone.getServiceStateTracker().mSS != null) { 1179 ServiceState ss = mDefaultPhone.getServiceStateTracker().mSS; 1180 mSS.setDataRegState(ss.getDataRegState()); 1181 mSS.setRilDataRadioTechnology(ss.getRilDataRadioTechnology()); 1182 Rlog.d(LOG_TAG, "updateDataServiceState: defSs = " + ss + " imsSs = " + mSS); 1183 } 1184 } 1185 1186 @Override 1187 public void handleMessage (Message msg) { 1188 AsyncResult ar = (AsyncResult) msg.obj; 1189 Message onComplete; 1190 1191 if (DBG) Rlog.d(LOG_TAG, "handleMessage what=" + msg.what); 1192 switch (msg.what) { 1193 case EVENT_SET_CALL_FORWARD_DONE: 1194 IccRecords r = getIccRecords(); 1195 Cf cf = (Cf) ar.userObj; 1196 if (cf.mIsCfu && ar.exception == null && r != null) { 1197 setVoiceCallForwardingFlag(r, 1, msg.arg1 == 1, cf.mSetCfNumber); 1198 } 1199 sendResponse(cf.mOnComplete, null, ar.exception); 1200 break; 1201 1202 case EVENT_GET_CALL_FORWARD_DONE: 1203 CallForwardInfo[] cfInfos = null; 1204 if (ar.exception == null) { 1205 cfInfos = handleCfQueryResult((ImsCallForwardInfo[])ar.result); 1206 } 1207 sendResponse((Message) ar.userObj, cfInfos, ar.exception); 1208 break; 1209 1210 case EVENT_GET_CALL_BARRING_DONE: 1211 case EVENT_GET_CALL_WAITING_DONE: 1212 int[] ssInfos = null; 1213 if (ar.exception == null) { 1214 if (msg.what == EVENT_GET_CALL_BARRING_DONE) { 1215 ssInfos = handleCbQueryResult((ImsSsInfo[])ar.result); 1216 } else if (msg.what == EVENT_GET_CALL_WAITING_DONE) { 1217 ssInfos = handleCwQueryResult((ImsSsInfo[])ar.result); 1218 } 1219 } 1220 sendResponse((Message) ar.userObj, ssInfos, ar.exception); 1221 break; 1222 1223 case EVENT_GET_CLIR_DONE: 1224 Bundle ssInfo = (Bundle) ar.result; 1225 int[] clirInfo = null; 1226 if (ssInfo != null) { 1227 clirInfo = ssInfo.getIntArray(ImsPhoneMmiCode.UT_BUNDLE_KEY_CLIR); 1228 } 1229 sendResponse((Message) ar.userObj, clirInfo, ar.exception); 1230 break; 1231 1232 case EVENT_SET_CLIR_DONE: 1233 if (ar.exception == null) { 1234 saveClirSetting(msg.arg1); 1235 } 1236 // (Intentional fallthrough) 1237 case EVENT_SET_CALL_BARRING_DONE: 1238 case EVENT_SET_CALL_WAITING_DONE: 1239 sendResponse((Message) ar.userObj, null, ar.exception); 1240 break; 1241 1242 case EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED: 1243 if (DBG) Rlog.d(LOG_TAG, "EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED"); 1244 updateDataServiceState(); 1245 break; 1246 1247 default: 1248 super.handleMessage(msg); 1249 break; 1250 } 1251 } 1252 1253 /** 1254 * Listen to the IMS ECBM state change 1255 */ 1256 ImsEcbmStateListener mImsEcbmStateListener = 1257 new ImsEcbmStateListener() { 1258 @Override 1259 public void onECBMEntered() { 1260 if (DBG) Rlog.d(LOG_TAG, "onECBMEntered"); 1261 handleEnterEmergencyCallbackMode(); 1262 } 1263 1264 @Override 1265 public void onECBMExited() { 1266 if (DBG) Rlog.d(LOG_TAG, "onECBMExited"); 1267 handleExitEmergencyCallbackMode(); 1268 } 1269 }; 1270 1271 public boolean isInEmergencyCall() { 1272 return mCT.isInEmergencyCall(); 1273 } 1274 1275 public boolean isInEcm() { 1276 return mIsPhoneInEcmState; 1277 } 1278 1279 void sendEmergencyCallbackModeChange() { 1280 // Send an Intent 1281 Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); 1282 intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, mIsPhoneInEcmState); 1283 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId()); 1284 ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL); 1285 if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange"); 1286 } 1287 1288 @Override 1289 public void exitEmergencyCallbackMode() { 1290 if (mWakeLock.isHeld()) { 1291 mWakeLock.release(); 1292 } 1293 if (DBG) Rlog.d(LOG_TAG, "exitEmergencyCallbackMode()"); 1294 1295 // Send a message which will invoke handleExitEmergencyCallbackMode 1296 ImsEcbm ecbm; 1297 try { 1298 ecbm = mCT.getEcbmInterface(); 1299 ecbm.exitEmergencyCallbackMode(); 1300 } catch (ImsException e) { 1301 e.printStackTrace(); 1302 } 1303 } 1304 1305 private void handleEnterEmergencyCallbackMode() { 1306 if (DBG) { 1307 Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= " 1308 + mIsPhoneInEcmState); 1309 } 1310 // if phone is not in Ecm mode, and it's changed to Ecm mode 1311 if (mIsPhoneInEcmState == false) { 1312 mIsPhoneInEcmState = true; 1313 // notify change 1314 sendEmergencyCallbackModeChange(); 1315 setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "true"); 1316 1317 // Post this runnable so we will automatically exit 1318 // if no one invokes exitEmergencyCallbackMode() directly. 1319 long delayInMillis = SystemProperties.getLong( 1320 TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE); 1321 postDelayed(mExitEcmRunnable, delayInMillis); 1322 // We don't want to go to sleep while in Ecm 1323 mWakeLock.acquire(); 1324 } 1325 } 1326 1327 private void handleExitEmergencyCallbackMode() { 1328 if (DBG) { 1329 Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode: mIsPhoneInEcmState = " 1330 + mIsPhoneInEcmState); 1331 } 1332 // Remove pending exit Ecm runnable, if any 1333 removeCallbacks(mExitEcmRunnable); 1334 1335 if (mEcmExitRespRegistrant != null) { 1336 mEcmExitRespRegistrant.notifyResult(Boolean.TRUE); 1337 } 1338 if (mIsPhoneInEcmState) { 1339 mIsPhoneInEcmState = false; 1340 setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "false"); 1341 } 1342 // send an Intent 1343 sendEmergencyCallbackModeChange(); 1344 } 1345 1346 /** 1347 * Handle to cancel or restart Ecm timer in emergency call back mode if action is 1348 * CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled; otherwise, restart 1349 * Ecm timer and notify apps the timer is restarted. 1350 */ 1351 void handleTimerInEmergencyCallbackMode(int action) { 1352 switch (action) { 1353 case CANCEL_ECM_TIMER: 1354 removeCallbacks(mExitEcmRunnable); 1355 ((GsmCdmaPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.TRUE); 1356 break; 1357 case RESTART_ECM_TIMER: 1358 long delayInMillis = SystemProperties.getLong( 1359 TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE); 1360 postDelayed(mExitEcmRunnable, delayInMillis); 1361 ((GsmCdmaPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.FALSE); 1362 break; 1363 default: 1364 Rlog.e(LOG_TAG, "handleTimerInEmergencyCallbackMode, unsupported action " + action); 1365 } 1366 } 1367 1368 public void setOnEcbModeExitResponse(Handler h, int what, Object obj) { 1369 mEcmExitRespRegistrant = new Registrant(h, what, obj); 1370 } 1371 1372 public void unsetOnEcbModeExitResponse(Handler h) { 1373 mEcmExitRespRegistrant.clear(); 1374 } 1375 1376 public void onFeatureCapabilityChanged() { 1377 mDefaultPhone.getServiceStateTracker().onImsCapabilityChanged(); 1378 } 1379 1380 public boolean isVolteEnabled() { 1381 return mCT.isVolteEnabled(); 1382 } 1383 1384 @Override 1385 public boolean isWifiCallingEnabled() { 1386 return mCT.isVowifiEnabled(); 1387 } 1388 1389 @Override 1390 public boolean isVideoEnabled() { 1391 return mCT.isVideoCallEnabled(); 1392 } 1393 1394 @Override 1395 public Phone getDefaultPhone() { 1396 return mDefaultPhone; 1397 } 1398 1399 public boolean isImsRegistered() { 1400 return mImsRegistered; 1401 } 1402 1403 public void setImsRegistered(boolean value) { 1404 mImsRegistered = value; 1405 } 1406 1407 @Override 1408 public void callEndCleanupHandOverCallIfAny() { 1409 mCT.callEndCleanupHandOverCallIfAny(); 1410 } 1411 1412 private BroadcastReceiver mResultReceiver = new BroadcastReceiver() { 1413 @Override 1414 public void onReceive(Context context, Intent intent) { 1415 // Add notification only if alert was not shown by WfcSettings 1416 if (getResultCode() == Activity.RESULT_OK) { 1417 // Default result code (as passed to sendOrderedBroadcast) 1418 // means that intent was not received by WfcSettings. 1419 1420 CharSequence title = intent.getCharSequenceExtra(EXTRA_KEY_ALERT_TITLE); 1421 CharSequence messageAlert = intent.getCharSequenceExtra(EXTRA_KEY_ALERT_MESSAGE); 1422 CharSequence messageNotification = intent.getCharSequenceExtra(EXTRA_KEY_NOTIFICATION_MESSAGE); 1423 1424 Intent resultIntent = new Intent(Intent.ACTION_MAIN); 1425 resultIntent.setClassName("com.android.settings", 1426 "com.android.settings.Settings$WifiCallingSettingsActivity"); 1427 resultIntent.putExtra(EXTRA_KEY_ALERT_SHOW, true); 1428 resultIntent.putExtra(EXTRA_KEY_ALERT_TITLE, title); 1429 resultIntent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert); 1430 PendingIntent resultPendingIntent = 1431 PendingIntent.getActivity( 1432 mContext, 1433 0, 1434 resultIntent, 1435 PendingIntent.FLAG_UPDATE_CURRENT 1436 ); 1437 1438 final Notification notification = 1439 new Notification.Builder(mContext) 1440 .setSmallIcon(android.R.drawable.stat_sys_warning) 1441 .setContentTitle(title) 1442 .setContentText(messageNotification) 1443 .setAutoCancel(true) 1444 .setContentIntent(resultPendingIntent) 1445 .setStyle(new Notification.BigTextStyle().bigText(messageNotification)) 1446 .build(); 1447 final String notificationTag = "wifi_calling"; 1448 final int notificationId = 1; 1449 1450 NotificationManager notificationManager = 1451 (NotificationManager) mContext.getSystemService( 1452 Context.NOTIFICATION_SERVICE); 1453 notificationManager.notify(notificationTag, notificationId, 1454 notification); 1455 } 1456 } 1457 }; 1458 1459 /** 1460 * Show notification in case of some error codes. 1461 */ 1462 public void processDisconnectReason(ImsReasonInfo imsReasonInfo) { 1463 if (imsReasonInfo.mCode == imsReasonInfo.CODE_REGISTRATION_ERROR 1464 && imsReasonInfo.mExtraMessage != null) { 1465 1466 final String[] wfcOperatorErrorCodes = 1467 mContext.getResources().getStringArray( 1468 com.android.internal.R.array.wfcOperatorErrorCodes); 1469 final String[] wfcOperatorErrorAlertMessages = 1470 mContext.getResources().getStringArray( 1471 com.android.internal.R.array.wfcOperatorErrorAlertMessages); 1472 final String[] wfcOperatorErrorNotificationMessages = 1473 mContext.getResources().getStringArray( 1474 com.android.internal.R.array.wfcOperatorErrorNotificationMessages); 1475 1476 for (int i = 0; i < wfcOperatorErrorCodes.length; i++) { 1477 // Match error code. 1478 if (!imsReasonInfo.mExtraMessage.startsWith( 1479 wfcOperatorErrorCodes[i])) { 1480 continue; 1481 } 1482 // If there is no delimiter at the end of error code string 1483 // then we need to verify that we are not matching partial code. 1484 // EXAMPLE: "REG9" must not match "REG99". 1485 // NOTE: Error code must not be empty. 1486 int codeStringLength = wfcOperatorErrorCodes[i].length(); 1487 char lastChar = wfcOperatorErrorCodes[i].charAt(codeStringLength-1); 1488 if (Character.isLetterOrDigit(lastChar)) { 1489 if (imsReasonInfo.mExtraMessage.length() > codeStringLength) { 1490 char nextChar = imsReasonInfo.mExtraMessage.charAt(codeStringLength); 1491 if (Character.isLetterOrDigit(nextChar)) { 1492 continue; 1493 } 1494 } 1495 } 1496 1497 final CharSequence title = mContext.getText( 1498 com.android.internal.R.string.wfcRegErrorTitle); 1499 1500 CharSequence messageAlert = imsReasonInfo.mExtraMessage; 1501 CharSequence messageNotification = imsReasonInfo.mExtraMessage; 1502 if (!wfcOperatorErrorAlertMessages[i].isEmpty()) { 1503 messageAlert = wfcOperatorErrorAlertMessages[i]; 1504 } 1505 if (!wfcOperatorErrorNotificationMessages[i].isEmpty()) { 1506 messageNotification = wfcOperatorErrorNotificationMessages[i]; 1507 } 1508 1509 // UX requirement is to disable WFC in case of "permanent" registration failures. 1510 ImsManager.setWfcSetting(mContext, false); 1511 1512 // If WfcSettings are active then alert will be shown 1513 // otherwise notification will be added. 1514 Intent intent = new Intent(ImsManager.ACTION_IMS_REGISTRATION_ERROR); 1515 intent.putExtra(EXTRA_KEY_ALERT_TITLE, title); 1516 intent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert); 1517 intent.putExtra(EXTRA_KEY_NOTIFICATION_MESSAGE, messageNotification); 1518 mContext.sendOrderedBroadcast(intent, null, mResultReceiver, 1519 null, Activity.RESULT_OK, null, null); 1520 1521 // We can only match a single error code 1522 // so should break the loop after a successful match. 1523 break; 1524 } 1525 } 1526 } 1527 1528 public boolean isUtEnabled() { 1529 return mCT.isUtEnabled(); 1530 } 1531 1532 @Override 1533 public void sendEmergencyCallStateChange(boolean callActive) { 1534 mDefaultPhone.sendEmergencyCallStateChange(callActive); 1535 } 1536 1537 @Override 1538 public void setBroadcastEmergencyCallStateChanges(boolean broadcast) { 1539 mDefaultPhone.setBroadcastEmergencyCallStateChanges(broadcast); 1540 } 1541 1542 @Override 1543 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1544 pw.println("ImsPhone extends:"); 1545 super.dump(fd, pw, args); 1546 pw.flush(); 1547 1548 pw.println("ImsPhone:"); 1549 pw.println(" mDefaultPhone = " + mDefaultPhone); 1550 pw.println(" mPendingMMIs = " + mPendingMMIs); 1551 pw.println(" mPostDialHandler = " + mPostDialHandler); 1552 pw.println(" mSS = " + mSS); 1553 pw.println(" mWakeLock = " + mWakeLock); 1554 pw.println(" mIsPhoneInEcmState = " + mIsPhoneInEcmState); 1555 pw.println(" mEcmExitRespRegistrant = " + mEcmExitRespRegistrant); 1556 pw.println(" mSilentRedialRegistrants = " + mSilentRedialRegistrants); 1557 pw.println(" mImsRegistered = " + mImsRegistered); 1558 pw.println(" mSsnRegistrants = " + mSsnRegistrants); 1559 pw.flush(); 1560 } 1561} 1562