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