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