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