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