ImsPhone.java revision cc8ea4abbe4f1f755dc5cf1b576d51ceb24fe79d
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 public void getCallBarring(String facility, Message onComplete) { 820 if (DBG) Rlog.d(LOG_TAG, "getCallBarring facility=" + facility); 821 Message resp; 822 resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, onComplete); 823 824 try { 825 ImsUtInterface ut = mCT.getUtInterface(); 826 ut.queryCallBarring(getCBTypeFromFacility(facility), resp); 827 } catch (ImsException e) { 828 sendErrorResponse(onComplete, e); 829 } 830 } 831 832 public void setCallBarring(String facility, boolean lockState, String password, Message 833 onComplete) { 834 if (DBG) Rlog.d(LOG_TAG, "setCallBarring facility=" + facility 835 + ", lockState=" + lockState); 836 Message resp; 837 resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, onComplete); 838 839 int action; 840 if (lockState) { 841 action = CommandsInterface.CF_ACTION_ENABLE; 842 } 843 else { 844 action = CommandsInterface.CF_ACTION_DISABLE; 845 } 846 847 try { 848 ImsUtInterface ut = mCT.getUtInterface(); 849 // password is not required with Ut interface 850 ut.updateCallBarring(getCBTypeFromFacility(facility), action, resp, null); 851 } catch (ImsException e) { 852 sendErrorResponse(onComplete, e); 853 } 854 } 855 856 @Override 857 public void sendUssdResponse(String ussdMessge) { 858 Rlog.d(LOG_TAG, "sendUssdResponse"); 859 ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessge, this); 860 mPendingMMIs.add(mmi); 861 mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); 862 mmi.sendUssd(ussdMessge); 863 } 864 865 public void sendUSSD(String ussdString, Message response) { 866 mCT.sendUSSD(ussdString, response); 867 } 868 869 @Override 870 public void cancelUSSD() { 871 mCT.cancelUSSD(); 872 } 873 874 private void sendErrorResponse(Message onComplete) { 875 Rlog.d(LOG_TAG, "sendErrorResponse"); 876 if (onComplete != null) { 877 AsyncResult.forMessage(onComplete, null, 878 new CommandException(CommandException.Error.GENERIC_FAILURE)); 879 onComplete.sendToTarget(); 880 } 881 } 882 883 /* package */ 884 void sendErrorResponse(Message onComplete, Throwable e) { 885 Rlog.d(LOG_TAG, "sendErrorResponse"); 886 if (onComplete != null) { 887 AsyncResult.forMessage(onComplete, null, getCommandException(e)); 888 onComplete.sendToTarget(); 889 } 890 } 891 892 private CommandException getCommandException(int code, String errorString) { 893 Rlog.d(LOG_TAG, "getCommandException code= " + code 894 + ", errorString= " + errorString); 895 CommandException.Error error = CommandException.Error.GENERIC_FAILURE; 896 897 switch(code) { 898 case ImsReasonInfo.CODE_UT_NOT_SUPPORTED: 899 error = CommandException.Error.REQUEST_NOT_SUPPORTED; 900 break; 901 case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH: 902 error = CommandException.Error.PASSWORD_INCORRECT; 903 break; 904 case ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE: 905 error = CommandException.Error.RADIO_NOT_AVAILABLE; 906 default: 907 break; 908 } 909 910 return new CommandException(error, errorString); 911 } 912 913 private CommandException getCommandException(Throwable e) { 914 CommandException ex = null; 915 916 if (e instanceof ImsException) { 917 ex = getCommandException(((ImsException)e).getCode(), e.getMessage()); 918 } else { 919 Rlog.d(LOG_TAG, "getCommandException generic failure"); 920 ex = new CommandException(CommandException.Error.GENERIC_FAILURE); 921 } 922 return ex; 923 } 924 925 private void 926 onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) { 927 Rlog.d(LOG_TAG, "onNetworkInitiatedUssd"); 928 mMmiCompleteRegistrants.notifyRegistrants( 929 new AsyncResult(null, mmi, null)); 930 } 931 932 /* package */ 933 void onIncomingUSSD(int ussdMode, String ussdMessage) { 934 if (DBG) Rlog.d(LOG_TAG, "onIncomingUSSD ussdMode=" + ussdMode); 935 936 boolean isUssdError; 937 boolean isUssdRequest; 938 939 isUssdRequest 940 = (ussdMode == CommandsInterface.USSD_MODE_REQUEST); 941 942 isUssdError 943 = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY 944 && ussdMode != CommandsInterface.USSD_MODE_REQUEST); 945 946 ImsPhoneMmiCode found = null; 947 for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) { 948 if(mPendingMMIs.get(i).isPendingUSSD()) { 949 found = mPendingMMIs.get(i); 950 break; 951 } 952 } 953 954 if (found != null) { 955 // Complete pending USSD 956 if (isUssdError) { 957 found.onUssdFinishedError(); 958 } else { 959 found.onUssdFinished(ussdMessage, isUssdRequest); 960 } 961 } else { // pending USSD not found 962 // The network may initiate its own USSD request 963 964 // ignore everything that isnt a Notify or a Request 965 // also, discard if there is no message to present 966 if (!isUssdError && ussdMessage != null) { 967 ImsPhoneMmiCode mmi; 968 mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage, 969 isUssdRequest, 970 this); 971 onNetworkInitiatedUssd(mmi); 972 } 973 } 974 } 975 976 /** 977 * Removes the given MMI from the pending list and notifies 978 * registrants that it is complete. 979 * @param mmi MMI that is done 980 */ 981 public void onMMIDone(ImsPhoneMmiCode mmi) { 982 /* Only notify complete if it's on the pending list. 983 * Otherwise, it's already been handled (eg, previously canceled). 984 * The exception is cancellation of an incoming USSD-REQUEST, which is 985 * not on the list. 986 */ 987 if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest()) { 988 mMmiCompleteRegistrants.notifyRegistrants( 989 new AsyncResult(null, mmi, null)); 990 } 991 } 992 993 @Override 994 public ArrayList<Connection> getHandoverConnection() { 995 ArrayList<Connection> connList = new ArrayList<Connection>(); 996 // Add all foreground call connections 997 connList.addAll(getForegroundCall().mConnections); 998 // Add all background call connections 999 connList.addAll(getBackgroundCall().mConnections); 1000 // Add all background call connections 1001 connList.addAll(getRingingCall().mConnections); 1002 if (connList.size() > 0) { 1003 return connList; 1004 } else { 1005 return null; 1006 } 1007 } 1008 1009 @Override 1010 public void notifySrvccState(Call.SrvccState state) { 1011 mCT.notifySrvccState(state); 1012 } 1013 1014 /* package */ void 1015 initiateSilentRedial() { 1016 String result = mLastDialString; 1017 AsyncResult ar = new AsyncResult(null, result, null); 1018 if (ar != null) { 1019 mSilentRedialRegistrants.notifyRegistrants(ar); 1020 } 1021 } 1022 1023 @Override 1024 public void registerForSilentRedial(Handler h, int what, Object obj) { 1025 mSilentRedialRegistrants.addUnique(h, what, obj); 1026 } 1027 1028 @Override 1029 public void unregisterForSilentRedial(Handler h) { 1030 mSilentRedialRegistrants.remove(h); 1031 } 1032 1033 @Override 1034 public void registerForSuppServiceNotification(Handler h, int what, Object obj) { 1035 mSsnRegistrants.addUnique(h, what, obj); 1036 } 1037 1038 @Override 1039 public void unregisterForSuppServiceNotification(Handler h) { 1040 mSsnRegistrants.remove(h); 1041 } 1042 1043 @Override 1044 public int getSubId() { 1045 return mDefaultPhone.getSubId(); 1046 } 1047 1048 @Override 1049 public int getPhoneId() { 1050 return mDefaultPhone.getPhoneId(); 1051 } 1052 1053 private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) { 1054 CallForwardInfo cfInfo = new CallForwardInfo(); 1055 cfInfo.status = info.mStatus; 1056 cfInfo.reason = getCFReasonFromCondition(info.mCondition); 1057 cfInfo.serviceClass = SERVICE_CLASS_VOICE; 1058 cfInfo.toa = info.mToA; 1059 cfInfo.number = info.mNumber; 1060 cfInfo.timeSeconds = info.mTimeSeconds; 1061 return cfInfo; 1062 } 1063 1064 private CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) { 1065 CallForwardInfo[] cfInfos = null; 1066 1067 if (infos != null && infos.length != 0) { 1068 cfInfos = new CallForwardInfo[infos.length]; 1069 } 1070 1071 IccRecords r = mDefaultPhone.getIccRecords(); 1072 if (infos == null || infos.length == 0) { 1073 if (r != null) { 1074 // Assume the default is not active 1075 // Set unconditional CFF in SIM to false 1076 setVoiceCallForwardingFlag(r, 1, false, null); 1077 } 1078 } else { 1079 for (int i = 0, s = infos.length; i < s; i++) { 1080 if (infos[i].mCondition == ImsUtInterface.CDIV_CF_UNCONDITIONAL) { 1081 if (r != null) { 1082 setVoiceCallForwardingFlag(r, 1, (infos[i].mStatus == 1), 1083 infos[i].mNumber); 1084 } 1085 } 1086 cfInfos[i] = getCallForwardInfo(infos[i]); 1087 } 1088 } 1089 1090 return cfInfos; 1091 } 1092 1093 private int[] handleCbQueryResult(ImsSsInfo[] infos) { 1094 int[] cbInfos = new int[1]; 1095 cbInfos[0] = SERVICE_CLASS_NONE; 1096 1097 if (infos[0].mStatus == 1) { 1098 cbInfos[0] = SERVICE_CLASS_VOICE; 1099 } 1100 1101 return cbInfos; 1102 } 1103 1104 private int[] handleCwQueryResult(ImsSsInfo[] infos) { 1105 int[] cwInfos = new int[2]; 1106 cwInfos[0] = 0; 1107 1108 if (infos[0].mStatus == 1) { 1109 cwInfos[0] = 1; 1110 cwInfos[1] = SERVICE_CLASS_VOICE; 1111 } 1112 1113 return cwInfos; 1114 } 1115 1116 private void 1117 sendResponse(Message onComplete, Object result, Throwable e) { 1118 if (onComplete != null) { 1119 CommandException ex = null; 1120 if (e != null) { 1121 ex = getCommandException(e); 1122 } 1123 AsyncResult.forMessage(onComplete, result, ex); 1124 onComplete.sendToTarget(); 1125 } 1126 } 1127 1128 private void updateDataServiceState() { 1129 if (mSS != null && mDefaultPhone.getServiceStateTracker() != null 1130 && mDefaultPhone.getServiceStateTracker().mSS != null) { 1131 ServiceState ss = mDefaultPhone.getServiceStateTracker().mSS; 1132 mSS.setDataRegState(ss.getDataRegState()); 1133 mSS.setRilDataRadioTechnology(ss.getRilDataRadioTechnology()); 1134 Rlog.d(LOG_TAG, "updateDataServiceState: defSs = " + ss + " imsSs = " + mSS); 1135 } 1136 } 1137 1138 @Override 1139 public void handleMessage(Message msg) { 1140 AsyncResult ar = (AsyncResult) msg.obj; 1141 1142 if (DBG) Rlog.d(LOG_TAG, "handleMessage what=" + msg.what); 1143 switch (msg.what) { 1144 case EVENT_SET_CALL_FORWARD_DONE: 1145 IccRecords r = mDefaultPhone.getIccRecords(); 1146 Cf cf = (Cf) ar.userObj; 1147 if (cf.mIsCfu && ar.exception == null && r != null) { 1148 setVoiceCallForwardingFlag(r, 1, msg.arg1 == 1, cf.mSetCfNumber); 1149 } 1150 sendResponse(cf.mOnComplete, null, ar.exception); 1151 break; 1152 1153 case EVENT_GET_CALL_FORWARD_DONE: 1154 CallForwardInfo[] cfInfos = null; 1155 if (ar.exception == null) { 1156 cfInfos = handleCfQueryResult((ImsCallForwardInfo[])ar.result); 1157 } 1158 sendResponse((Message) ar.userObj, cfInfos, ar.exception); 1159 break; 1160 1161 case EVENT_GET_CALL_BARRING_DONE: 1162 case EVENT_GET_CALL_WAITING_DONE: 1163 int[] ssInfos = null; 1164 if (ar.exception == null) { 1165 if (msg.what == EVENT_GET_CALL_BARRING_DONE) { 1166 ssInfos = handleCbQueryResult((ImsSsInfo[])ar.result); 1167 } else if (msg.what == EVENT_GET_CALL_WAITING_DONE) { 1168 ssInfos = handleCwQueryResult((ImsSsInfo[])ar.result); 1169 } 1170 } 1171 sendResponse((Message) ar.userObj, ssInfos, ar.exception); 1172 break; 1173 1174 case EVENT_GET_CLIR_DONE: 1175 Bundle ssInfo = (Bundle) ar.result; 1176 int[] clirInfo = null; 1177 if (ssInfo != null) { 1178 clirInfo = ssInfo.getIntArray(ImsPhoneMmiCode.UT_BUNDLE_KEY_CLIR); 1179 } 1180 sendResponse((Message) ar.userObj, clirInfo, ar.exception); 1181 break; 1182 1183 case EVENT_SET_CLIR_DONE: 1184 if (ar.exception == null) { 1185 saveClirSetting(msg.arg1); 1186 } 1187 // (Intentional fallthrough) 1188 case EVENT_SET_CALL_BARRING_DONE: 1189 case EVENT_SET_CALL_WAITING_DONE: 1190 sendResponse((Message) ar.userObj, null, ar.exception); 1191 break; 1192 1193 case EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED: 1194 if (DBG) Rlog.d(LOG_TAG, "EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED"); 1195 updateDataServiceState(); 1196 break; 1197 1198 default: 1199 super.handleMessage(msg); 1200 break; 1201 } 1202 } 1203 1204 /** 1205 * Listen to the IMS ECBM state change 1206 */ 1207 ImsEcbmStateListener mImsEcbmStateListener = 1208 new ImsEcbmStateListener() { 1209 @Override 1210 public void onECBMEntered() { 1211 if (DBG) Rlog.d(LOG_TAG, "onECBMEntered"); 1212 handleEnterEmergencyCallbackMode(); 1213 } 1214 1215 @Override 1216 public void onECBMExited() { 1217 if (DBG) Rlog.d(LOG_TAG, "onECBMExited"); 1218 handleExitEmergencyCallbackMode(); 1219 } 1220 }; 1221 1222 @Override 1223 public boolean isInEmergencyCall() { 1224 return mCT.isInEmergencyCall(); 1225 } 1226 1227 @Override 1228 public boolean isInEcm() { 1229 return mIsPhoneInEcmState; 1230 } 1231 1232 private void sendEmergencyCallbackModeChange() { 1233 // Send an Intent 1234 Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); 1235 intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, mIsPhoneInEcmState); 1236 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId()); 1237 ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL); 1238 if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange"); 1239 } 1240 1241 @Override 1242 public void exitEmergencyCallbackMode() { 1243 if (mWakeLock.isHeld()) { 1244 mWakeLock.release(); 1245 } 1246 if (DBG) Rlog.d(LOG_TAG, "exitEmergencyCallbackMode()"); 1247 1248 // Send a message which will invoke handleExitEmergencyCallbackMode 1249 ImsEcbm ecbm; 1250 try { 1251 ecbm = mCT.getEcbmInterface(); 1252 ecbm.exitEmergencyCallbackMode(); 1253 } catch (ImsException e) { 1254 e.printStackTrace(); 1255 } 1256 } 1257 1258 private void handleEnterEmergencyCallbackMode() { 1259 if (DBG) { 1260 Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= " 1261 + mIsPhoneInEcmState); 1262 } 1263 // if phone is not in Ecm mode, and it's changed to Ecm mode 1264 if (mIsPhoneInEcmState == false) { 1265 mIsPhoneInEcmState = true; 1266 // notify change 1267 sendEmergencyCallbackModeChange(); 1268 setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "true"); 1269 1270 // Post this runnable so we will automatically exit 1271 // if no one invokes exitEmergencyCallbackMode() directly. 1272 long delayInMillis = SystemProperties.getLong( 1273 TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE); 1274 postDelayed(mExitEcmRunnable, delayInMillis); 1275 // We don't want to go to sleep while in Ecm 1276 mWakeLock.acquire(); 1277 } 1278 } 1279 1280 private void handleExitEmergencyCallbackMode() { 1281 if (DBG) { 1282 Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode: mIsPhoneInEcmState = " 1283 + mIsPhoneInEcmState); 1284 } 1285 // Remove pending exit Ecm runnable, if any 1286 removeCallbacks(mExitEcmRunnable); 1287 1288 if (mEcmExitRespRegistrant != null) { 1289 mEcmExitRespRegistrant.notifyResult(Boolean.TRUE); 1290 } 1291 if (mIsPhoneInEcmState) { 1292 mIsPhoneInEcmState = false; 1293 setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "false"); 1294 } 1295 // send an Intent 1296 sendEmergencyCallbackModeChange(); 1297 } 1298 1299 /** 1300 * Handle to cancel or restart Ecm timer in emergency call back mode if action is 1301 * CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled; otherwise, restart 1302 * Ecm timer and notify apps the timer is restarted. 1303 */ 1304 void handleTimerInEmergencyCallbackMode(int action) { 1305 switch (action) { 1306 case CANCEL_ECM_TIMER: 1307 removeCallbacks(mExitEcmRunnable); 1308 ((GsmCdmaPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.TRUE); 1309 break; 1310 case RESTART_ECM_TIMER: 1311 long delayInMillis = SystemProperties.getLong( 1312 TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE); 1313 postDelayed(mExitEcmRunnable, delayInMillis); 1314 ((GsmCdmaPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.FALSE); 1315 break; 1316 default: 1317 Rlog.e(LOG_TAG, "handleTimerInEmergencyCallbackMode, unsupported action " + action); 1318 } 1319 } 1320 1321 @Override 1322 public void setOnEcbModeExitResponse(Handler h, int what, Object obj) { 1323 mEcmExitRespRegistrant = new Registrant(h, what, obj); 1324 } 1325 1326 @Override 1327 public void unsetOnEcbModeExitResponse(Handler h) { 1328 mEcmExitRespRegistrant.clear(); 1329 } 1330 1331 public void onFeatureCapabilityChanged() { 1332 mDefaultPhone.getServiceStateTracker().onImsCapabilityChanged(); 1333 } 1334 1335 @Override 1336 public boolean isVolteEnabled() { 1337 return mCT.isVolteEnabled(); 1338 } 1339 1340 @Override 1341 public boolean isWifiCallingEnabled() { 1342 return mCT.isVowifiEnabled(); 1343 } 1344 1345 @Override 1346 public boolean isVideoEnabled() { 1347 return mCT.isVideoCallEnabled(); 1348 } 1349 1350 @Override 1351 public Phone getDefaultPhone() { 1352 return mDefaultPhone; 1353 } 1354 1355 @Override 1356 public boolean isImsRegistered() { 1357 return mImsRegistered; 1358 } 1359 1360 public void setImsRegistered(boolean value) { 1361 mImsRegistered = value; 1362 } 1363 1364 @Override 1365 public void callEndCleanupHandOverCallIfAny() { 1366 mCT.callEndCleanupHandOverCallIfAny(); 1367 } 1368 1369 private BroadcastReceiver mResultReceiver = new BroadcastReceiver() { 1370 @Override 1371 public void onReceive(Context context, Intent intent) { 1372 // Add notification only if alert was not shown by WfcSettings 1373 if (getResultCode() == Activity.RESULT_OK) { 1374 // Default result code (as passed to sendOrderedBroadcast) 1375 // means that intent was not received by WfcSettings. 1376 1377 CharSequence title = intent.getCharSequenceExtra(EXTRA_KEY_ALERT_TITLE); 1378 CharSequence messageAlert = intent.getCharSequenceExtra(EXTRA_KEY_ALERT_MESSAGE); 1379 CharSequence messageNotification = intent.getCharSequenceExtra(EXTRA_KEY_NOTIFICATION_MESSAGE); 1380 1381 Intent resultIntent = new Intent(Intent.ACTION_MAIN); 1382 resultIntent.setClassName("com.android.settings", 1383 "com.android.settings.Settings$WifiCallingSettingsActivity"); 1384 resultIntent.putExtra(EXTRA_KEY_ALERT_SHOW, true); 1385 resultIntent.putExtra(EXTRA_KEY_ALERT_TITLE, title); 1386 resultIntent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert); 1387 PendingIntent resultPendingIntent = 1388 PendingIntent.getActivity( 1389 mContext, 1390 0, 1391 resultIntent, 1392 PendingIntent.FLAG_UPDATE_CURRENT 1393 ); 1394 1395 final Notification notification = 1396 new Notification.Builder(mContext) 1397 .setSmallIcon(android.R.drawable.stat_sys_warning) 1398 .setContentTitle(title) 1399 .setContentText(messageNotification) 1400 .setAutoCancel(true) 1401 .setContentIntent(resultPendingIntent) 1402 .setStyle(new Notification.BigTextStyle().bigText(messageNotification)) 1403 .build(); 1404 final String notificationTag = "wifi_calling"; 1405 final int notificationId = 1; 1406 1407 NotificationManager notificationManager = 1408 (NotificationManager) mContext.getSystemService( 1409 Context.NOTIFICATION_SERVICE); 1410 notificationManager.notify(notificationTag, notificationId, 1411 notification); 1412 } 1413 } 1414 }; 1415 1416 /** 1417 * Show notification in case of some error codes. 1418 */ 1419 public void processDisconnectReason(ImsReasonInfo imsReasonInfo) { 1420 if (imsReasonInfo.mCode == imsReasonInfo.CODE_REGISTRATION_ERROR 1421 && imsReasonInfo.mExtraMessage != null) { 1422 1423 final String[] wfcOperatorErrorCodes = 1424 mContext.getResources().getStringArray( 1425 com.android.internal.R.array.wfcOperatorErrorCodes); 1426 final String[] wfcOperatorErrorAlertMessages = 1427 mContext.getResources().getStringArray( 1428 com.android.internal.R.array.wfcOperatorErrorAlertMessages); 1429 final String[] wfcOperatorErrorNotificationMessages = 1430 mContext.getResources().getStringArray( 1431 com.android.internal.R.array.wfcOperatorErrorNotificationMessages); 1432 1433 for (int i = 0; i < wfcOperatorErrorCodes.length; i++) { 1434 // Match error code. 1435 if (!imsReasonInfo.mExtraMessage.startsWith( 1436 wfcOperatorErrorCodes[i])) { 1437 continue; 1438 } 1439 // If there is no delimiter at the end of error code string 1440 // then we need to verify that we are not matching partial code. 1441 // EXAMPLE: "REG9" must not match "REG99". 1442 // NOTE: Error code must not be empty. 1443 int codeStringLength = wfcOperatorErrorCodes[i].length(); 1444 char lastChar = wfcOperatorErrorCodes[i].charAt(codeStringLength-1); 1445 if (Character.isLetterOrDigit(lastChar)) { 1446 if (imsReasonInfo.mExtraMessage.length() > codeStringLength) { 1447 char nextChar = imsReasonInfo.mExtraMessage.charAt(codeStringLength); 1448 if (Character.isLetterOrDigit(nextChar)) { 1449 continue; 1450 } 1451 } 1452 } 1453 1454 final CharSequence title = mContext.getText( 1455 com.android.internal.R.string.wfcRegErrorTitle); 1456 1457 CharSequence messageAlert = imsReasonInfo.mExtraMessage; 1458 CharSequence messageNotification = imsReasonInfo.mExtraMessage; 1459 if (!wfcOperatorErrorAlertMessages[i].isEmpty()) { 1460 messageAlert = wfcOperatorErrorAlertMessages[i]; 1461 } 1462 if (!wfcOperatorErrorNotificationMessages[i].isEmpty()) { 1463 messageNotification = wfcOperatorErrorNotificationMessages[i]; 1464 } 1465 1466 // UX requirement is to disable WFC in case of "permanent" registration failures. 1467 ImsManager.setWfcSetting(mContext, false); 1468 1469 // If WfcSettings are active then alert will be shown 1470 // otherwise notification will be added. 1471 Intent intent = new Intent(ImsManager.ACTION_IMS_REGISTRATION_ERROR); 1472 intent.putExtra(EXTRA_KEY_ALERT_TITLE, title); 1473 intent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert); 1474 intent.putExtra(EXTRA_KEY_NOTIFICATION_MESSAGE, messageNotification); 1475 mContext.sendOrderedBroadcast(intent, null, mResultReceiver, 1476 null, Activity.RESULT_OK, null, null); 1477 1478 // We can only match a single error code 1479 // so should break the loop after a successful match. 1480 break; 1481 } 1482 } 1483 } 1484 1485 @Override 1486 public boolean isUtEnabled() { 1487 return mCT.isUtEnabled(); 1488 } 1489 1490 @Override 1491 public void sendEmergencyCallStateChange(boolean callActive) { 1492 mDefaultPhone.sendEmergencyCallStateChange(callActive); 1493 } 1494 1495 @Override 1496 public void setBroadcastEmergencyCallStateChanges(boolean broadcast) { 1497 mDefaultPhone.setBroadcastEmergencyCallStateChanges(broadcast); 1498 } 1499 1500 @Override 1501 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1502 pw.println("ImsPhone extends:"); 1503 super.dump(fd, pw, args); 1504 pw.flush(); 1505 1506 pw.println("ImsPhone:"); 1507 pw.println(" mDefaultPhone = " + mDefaultPhone); 1508 pw.println(" mPendingMMIs = " + mPendingMMIs); 1509 pw.println(" mPostDialHandler = " + mPostDialHandler); 1510 pw.println(" mSS = " + mSS); 1511 pw.println(" mWakeLock = " + mWakeLock); 1512 pw.println(" mIsPhoneInEcmState = " + mIsPhoneInEcmState); 1513 pw.println(" mEcmExitRespRegistrant = " + mEcmExitRespRegistrant); 1514 pw.println(" mSilentRedialRegistrants = " + mSilentRedialRegistrants); 1515 pw.println(" mImsRegistered = " + mImsRegistered); 1516 pw.println(" mSsnRegistrants = " + mSsnRegistrants); 1517 pw.flush(); 1518 } 1519} 1520