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