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