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