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