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