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