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