ImsPhone.java revision 8762e33ba987774e43aa9f4e216ebdb084d3a03e
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 private 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(Handler h, int what, Object obj) { 1086 mSsnRegistrants.addUnique(h, what, obj); 1087 } 1088 1089 @Override 1090 public void unregisterForSuppServiceNotification(Handler h) { 1091 mSsnRegistrants.remove(h); 1092 } 1093 1094 @Override 1095 public int getSubId() { 1096 return mDefaultPhone.getSubId(); 1097 } 1098 1099 @Override 1100 public int getPhoneId() { 1101 return mDefaultPhone.getPhoneId(); 1102 } 1103 1104 private IccRecords getIccRecords() { 1105 return mDefaultPhone.mIccRecords.get(); 1106 } 1107 1108 private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) { 1109 CallForwardInfo cfInfo = new CallForwardInfo(); 1110 cfInfo.status = info.mStatus; 1111 cfInfo.reason = getCFReasonFromCondition(info.mCondition); 1112 cfInfo.serviceClass = SERVICE_CLASS_VOICE; 1113 cfInfo.toa = info.mToA; 1114 cfInfo.number = info.mNumber; 1115 cfInfo.timeSeconds = info.mTimeSeconds; 1116 return cfInfo; 1117 } 1118 1119 private CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) { 1120 CallForwardInfo[] cfInfos = null; 1121 1122 if (infos != null && infos.length != 0) { 1123 cfInfos = new CallForwardInfo[infos.length]; 1124 } 1125 1126 IccRecords r = getIccRecords(); 1127 if (infos == null || infos.length == 0) { 1128 if (r != null) { 1129 // Assume the default is not active 1130 // Set unconditional CFF in SIM to false 1131 r.setVoiceCallForwardingFlag(1, false, null); 1132 } 1133 } else { 1134 for (int i = 0, s = infos.length; i < s; i++) { 1135 if (infos[i].mCondition == ImsUtInterface.CDIV_CF_UNCONDITIONAL) { 1136 if (r != null) { 1137 r.setVoiceCallForwardingFlag(1, (infos[i].mStatus == 1), 1138 infos[i].mNumber); 1139 } 1140 } 1141 cfInfos[i] = getCallForwardInfo(infos[i]); 1142 } 1143 } 1144 1145 return cfInfos; 1146 } 1147 1148 private int[] handleCbQueryResult(ImsSsInfo[] infos) { 1149 int[] cbInfos = new int[1]; 1150 cbInfos[0] = SERVICE_CLASS_NONE; 1151 1152 if (infos[0].mStatus == 1) { 1153 cbInfos[0] = SERVICE_CLASS_VOICE; 1154 } 1155 1156 return cbInfos; 1157 } 1158 1159 private int[] handleCwQueryResult(ImsSsInfo[] infos) { 1160 int[] cwInfos = new int[2]; 1161 cwInfos[0] = 0; 1162 1163 if (infos[0].mStatus == 1) { 1164 cwInfos[0] = 1; 1165 cwInfos[1] = SERVICE_CLASS_VOICE; 1166 } 1167 1168 return cwInfos; 1169 } 1170 1171 private void 1172 sendResponse(Message onComplete, Object result, Throwable e) { 1173 if (onComplete != null) { 1174 CommandException ex = null; 1175 if (e != null) { 1176 ex = getCommandException(e); 1177 AsyncResult.forMessage(onComplete, result, ex); 1178 } else { 1179 AsyncResult.forMessage(onComplete, result, null); 1180 } 1181 AsyncResult.forMessage(onComplete, result, ex); 1182 onComplete.sendToTarget(); 1183 } 1184 } 1185 1186 @Override 1187 public void handleMessage (Message msg) { 1188 AsyncResult ar = (AsyncResult) msg.obj; 1189 Message onComplete; 1190 1191 if (DBG) Rlog.d(LOG_TAG, "handleMessage what=" + msg.what); 1192 switch (msg.what) { 1193 case EVENT_SET_CALL_FORWARD_DONE: 1194 IccRecords r = getIccRecords(); 1195 Cf cf = (Cf) ar.userObj; 1196 if (cf.mIsCfu && ar.exception == null && r != null) { 1197 r.setVoiceCallForwardingFlag(1, msg.arg1 == 1, cf.mSetCfNumber); 1198 } 1199 sendResponse(cf.mOnComplete, null, ar.exception); 1200 break; 1201 1202 case EVENT_GET_CALL_FORWARD_DONE: 1203 CallForwardInfo[] cfInfos = null; 1204 if (ar.exception == null) { 1205 cfInfos = handleCfQueryResult((ImsCallForwardInfo[])ar.result); 1206 } 1207 sendResponse((Message) ar.userObj, cfInfos, ar.exception); 1208 break; 1209 1210 case EVENT_GET_CALL_BARRING_DONE: 1211 case EVENT_GET_CALL_WAITING_DONE: 1212 int[] ssInfos = null; 1213 if (ar.exception == null) { 1214 if (msg.what == EVENT_GET_CALL_BARRING_DONE) { 1215 ssInfos = handleCbQueryResult((ImsSsInfo[])ar.result); 1216 } else if (msg.what == EVENT_GET_CALL_WAITING_DONE) { 1217 ssInfos = handleCwQueryResult((ImsSsInfo[])ar.result); 1218 } 1219 } 1220 sendResponse((Message) ar.userObj, ssInfos, ar.exception); 1221 break; 1222 1223 case EVENT_GET_CLIR_DONE: 1224 Bundle ssInfo = (Bundle) ar.result; 1225 int[] clirInfo = null; 1226 if (ssInfo != null) { 1227 clirInfo = ssInfo.getIntArray(ImsPhoneMmiCode.UT_BUNDLE_KEY_CLIR); 1228 } 1229 sendResponse((Message) ar.userObj, clirInfo, ar.exception); 1230 break; 1231 1232 case EVENT_SET_CLIR_DONE: 1233 case EVENT_SET_CALL_BARRING_DONE: 1234 case EVENT_SET_CALL_WAITING_DONE: 1235 sendResponse((Message) ar.userObj, null, ar.exception); 1236 break; 1237 1238 default: 1239 super.handleMessage(msg); 1240 break; 1241 } 1242 } 1243 1244 /** 1245 * Listen to the IMS ECBM state change 1246 */ 1247 ImsEcbmStateListener mImsEcbmStateListener = 1248 new ImsEcbmStateListener() { 1249 @Override 1250 public void onECBMEntered() { 1251 if (DBG) Rlog.d(LOG_TAG, "onECBMEntered"); 1252 handleEnterEmergencyCallbackMode(); 1253 } 1254 1255 @Override 1256 public void onECBMExited() { 1257 if (DBG) Rlog.d(LOG_TAG, "onECBMExited"); 1258 handleExitEmergencyCallbackMode(); 1259 } 1260 }; 1261 1262 public boolean isInEmergencyCall() { 1263 return mCT.isInEmergencyCall(); 1264 } 1265 1266 public boolean isInEcm() { 1267 return mIsPhoneInEcmState; 1268 } 1269 1270 void sendEmergencyCallbackModeChange() { 1271 // Send an Intent 1272 Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); 1273 intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, mIsPhoneInEcmState); 1274 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId()); 1275 ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL); 1276 if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange"); 1277 } 1278 1279 @Override 1280 public void exitEmergencyCallbackMode() { 1281 if (mWakeLock.isHeld()) { 1282 mWakeLock.release(); 1283 } 1284 if (DBG) Rlog.d(LOG_TAG, "exitEmergencyCallbackMode()"); 1285 1286 // Send a message which will invoke handleExitEmergencyCallbackMode 1287 ImsEcbm ecbm; 1288 try { 1289 ecbm = mCT.getEcbmInterface(); 1290 ecbm.exitEmergencyCallbackMode(); 1291 } catch (ImsException e) { 1292 e.printStackTrace(); 1293 } 1294 } 1295 1296 private void handleEnterEmergencyCallbackMode() { 1297 if (DBG) { 1298 Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= " 1299 + mIsPhoneInEcmState); 1300 } 1301 // if phone is not in Ecm mode, and it's changed to Ecm mode 1302 if (mIsPhoneInEcmState == false) { 1303 mIsPhoneInEcmState = true; 1304 // notify change 1305 sendEmergencyCallbackModeChange(); 1306 setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "true"); 1307 1308 // Post this runnable so we will automatically exit 1309 // if no one invokes exitEmergencyCallbackMode() directly. 1310 long delayInMillis = SystemProperties.getLong( 1311 TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE); 1312 postDelayed(mExitEcmRunnable, delayInMillis); 1313 // We don't want to go to sleep while in Ecm 1314 mWakeLock.acquire(); 1315 } 1316 } 1317 1318 private void handleExitEmergencyCallbackMode() { 1319 if (DBG) { 1320 Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode: mIsPhoneInEcmState = " 1321 + mIsPhoneInEcmState); 1322 } 1323 // Remove pending exit Ecm runnable, if any 1324 removeCallbacks(mExitEcmRunnable); 1325 1326 if (mEcmExitRespRegistrant != null) { 1327 mEcmExitRespRegistrant.notifyResult(Boolean.TRUE); 1328 } 1329 if (mIsPhoneInEcmState) { 1330 mIsPhoneInEcmState = false; 1331 setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "false"); 1332 } 1333 // send an Intent 1334 sendEmergencyCallbackModeChange(); 1335 } 1336 1337 /** 1338 * Handle to cancel or restart Ecm timer in emergency call back mode if action is 1339 * CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled; otherwise, restart 1340 * Ecm timer and notify apps the timer is restarted. 1341 */ 1342 void handleTimerInEmergencyCallbackMode(int action) { 1343 switch (action) { 1344 case CANCEL_ECM_TIMER: 1345 removeCallbacks(mExitEcmRunnable); 1346 if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) { 1347 ((GSMPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.TRUE); 1348 } else { // Should be CDMA - also go here by default 1349 ((CDMAPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.TRUE); 1350 } 1351 break; 1352 case RESTART_ECM_TIMER: 1353 long delayInMillis = SystemProperties.getLong( 1354 TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE); 1355 postDelayed(mExitEcmRunnable, delayInMillis); 1356 if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) { 1357 ((GSMPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.FALSE); 1358 } else { // Should be CDMA - also go here by default 1359 ((CDMAPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.FALSE); 1360 } 1361 break; 1362 default: 1363 Rlog.e(LOG_TAG, "handleTimerInEmergencyCallbackMode, unsupported action " + action); 1364 } 1365 } 1366 1367 public void setOnEcbModeExitResponse(Handler h, int what, Object obj) { 1368 mEcmExitRespRegistrant = new Registrant(h, what, obj); 1369 } 1370 1371 public void unsetOnEcbModeExitResponse(Handler h) { 1372 mEcmExitRespRegistrant.clear(); 1373 } 1374 1375 public void onFeatureCapabilityChanged() { 1376 mDefaultPhone.getServiceStateTracker().onImsCapabilityChanged(); 1377 } 1378 1379 public boolean isVolteEnabled() { 1380 return mCT.isVolteEnabled(); 1381 } 1382 1383 public boolean isVowifiEnabled() { 1384 return mCT.isVowifiEnabled(); 1385 } 1386 1387 public boolean isVideoCallEnabled() { 1388 return mCT.isVideoCallEnabled(); 1389 } 1390 1391 public Phone getDefaultPhone() { 1392 return mDefaultPhone; 1393 } 1394 1395 public boolean isImsRegistered() { 1396 return mImsRegistered; 1397 } 1398 1399 public void setImsRegistered(boolean value) { 1400 mImsRegistered = value; 1401 } 1402 1403 public void callEndCleanupHandOverCallIfAny() { 1404 mCT.callEndCleanupHandOverCallIfAny(); 1405 } 1406 1407 private BroadcastReceiver mResultReceiver = new BroadcastReceiver() { 1408 @Override 1409 public void onReceive(Context context, Intent intent) { 1410 // Add notification only if alert was not shown by WfcSettings 1411 if (getResultCode() == Activity.RESULT_OK) { 1412 // Default result code (as passed to sendOrderedBroadcast) 1413 // means that intent was not received by WfcSettings. 1414 1415 CharSequence title = intent.getCharSequenceExtra(EXTRA_KEY_ALERT_TITLE); 1416 CharSequence messageAlert = intent.getCharSequenceExtra(EXTRA_KEY_ALERT_MESSAGE); 1417 CharSequence messageNotification = intent.getCharSequenceExtra(EXTRA_KEY_NOTIFICATION_MESSAGE); 1418 1419 Intent resultIntent = new Intent(Intent.ACTION_MAIN); 1420 resultIntent.setClassName("com.android.settings", 1421 "com.android.settings.Settings$WifiCallingSettingsActivity"); 1422 resultIntent.putExtra(EXTRA_KEY_ALERT_SHOW, true); 1423 resultIntent.putExtra(EXTRA_KEY_ALERT_TITLE, title); 1424 resultIntent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert); 1425 PendingIntent resultPendingIntent = 1426 PendingIntent.getActivity( 1427 mContext, 1428 0, 1429 resultIntent, 1430 PendingIntent.FLAG_UPDATE_CURRENT 1431 ); 1432 1433 final Notification notification = 1434 new Notification.Builder(mContext) 1435 .setSmallIcon(android.R.drawable.stat_sys_warning) 1436 .setContentTitle(title) 1437 .setContentText(messageNotification) 1438 .setAutoCancel(true) 1439 .setContentIntent(resultPendingIntent) 1440 .setStyle(new Notification.BigTextStyle().bigText(messageNotification)) 1441 .build(); 1442 final String notificationTag = "wifi_calling"; 1443 final int notificationId = 1; 1444 1445 NotificationManager notificationManager = 1446 (NotificationManager) mContext.getSystemService( 1447 Context.NOTIFICATION_SERVICE); 1448 notificationManager.notify(notificationTag, notificationId, 1449 notification); 1450 } 1451 } 1452 }; 1453 1454 /** 1455 * Show notification in case of some error codes. 1456 */ 1457 public void processDisconnectReason(ImsReasonInfo imsReasonInfo) { 1458 if (imsReasonInfo.mCode == imsReasonInfo.CODE_REGISTRATION_ERROR 1459 && imsReasonInfo.mExtraMessage != null) { 1460 1461 final String[] wfcOperatorErrorCodes = 1462 mContext.getResources().getStringArray( 1463 com.android.internal.R.array.wfcOperatorErrorCodes); 1464 final String[] wfcOperatorErrorAlertMessages = 1465 mContext.getResources().getStringArray( 1466 com.android.internal.R.array.wfcOperatorErrorAlertMessages); 1467 final String[] wfcOperatorErrorNotificationMessages = 1468 mContext.getResources().getStringArray( 1469 com.android.internal.R.array.wfcOperatorErrorNotificationMessages); 1470 1471 for (int i = 0; i < wfcOperatorErrorCodes.length; i++) { 1472 // Match error code. 1473 if (!imsReasonInfo.mExtraMessage.startsWith( 1474 wfcOperatorErrorCodes[i])) { 1475 continue; 1476 } 1477 // If there is no delimiter at the end of error code string 1478 // then we need to verify that we are not matching partial code. 1479 // EXAMPLE: "REG9" must not match "REG99". 1480 // NOTE: Error code must not be empty. 1481 int codeStringLength = wfcOperatorErrorCodes[i].length(); 1482 char lastChar = wfcOperatorErrorCodes[i].charAt(codeStringLength-1); 1483 if (Character.isLetterOrDigit(lastChar)) { 1484 if (imsReasonInfo.mExtraMessage.length() > codeStringLength) { 1485 char nextChar = imsReasonInfo.mExtraMessage.charAt(codeStringLength); 1486 if (Character.isLetterOrDigit(nextChar)) { 1487 continue; 1488 } 1489 } 1490 } 1491 1492 final CharSequence title = mContext.getText( 1493 com.android.internal.R.string.wfcRegErrorTitle); 1494 1495 CharSequence messageAlert = imsReasonInfo.mExtraMessage; 1496 CharSequence messageNotification = imsReasonInfo.mExtraMessage; 1497 if (!wfcOperatorErrorAlertMessages[i].isEmpty()) { 1498 messageAlert = wfcOperatorErrorAlertMessages[i]; 1499 } 1500 if (!wfcOperatorErrorNotificationMessages[i].isEmpty()) { 1501 messageNotification = wfcOperatorErrorNotificationMessages[i]; 1502 } 1503 1504 // UX requirement is to disable WFC in case of "permanent" registration failures. 1505 ImsManager.setWfcSetting(mContext, false); 1506 1507 // If WfcSettings are active then alert will be shown 1508 // otherwise notification will be added. 1509 Intent intent = new Intent(ImsManager.ACTION_IMS_REGISTRATION_ERROR); 1510 intent.putExtra(EXTRA_KEY_ALERT_TITLE, title); 1511 intent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert); 1512 intent.putExtra(EXTRA_KEY_NOTIFICATION_MESSAGE, messageNotification); 1513 mContext.sendOrderedBroadcast(intent, null, mResultReceiver, 1514 null, Activity.RESULT_OK, null, null); 1515 1516 // We can only match a single error code 1517 // so should break the loop after a successful match. 1518 break; 1519 } 1520 } 1521 } 1522} 1523