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