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