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