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