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