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 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 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 151 public ImsDialArgs.Builder setRttTextStream( 152 android.telecom.Connection.RttTextStream s) { 153 mRttTextStream = s; 154 return this; 155 } 156 157 public ImsDialArgs.Builder setClirMode(int clirMode) { 158 this.mClirMode = clirMode; 159 return this; 160 } 161 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 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 218 protected void setCurrentSubscriberUris(Uri[] currentSubscriberUris) { 219 this.mCurrentSubscriberUris = currentSubscriberUris; 220 } 221 222 @Override 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 236 Cf(String cfNumber, boolean isCfu, Message onComplete) { 237 mSetCfNumber = cfNumber; 238 mIsCfu = isCfu; 239 mOnComplete = onComplete; 240 } 241 } 242 243 // Constructors 244 public ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone) { 245 this(context, notifier, defaultPhone, false); 246 } 247 248 @VisibleForTesting 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 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 308 public ServiceState getServiceState() { 309 return mSS; 310 } 311 312 @VisibleForTesting 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 330 public CallTracker getCallTracker() { 331 return mCT; 332 } 333 334 public ImsExternalCallTracker getExternalCallTracker() { 335 return mExternalCallTracker; 336 } 337 338 @Override 339 public List<? extends ImsPhoneMmiCode> 340 getPendingMmiCodes() { 341 return mPendingMMIs; 342 } 343 344 @Override 345 public void 346 acceptCall(int videoState) throws CallStateException { 347 mCT.acceptCall(videoState); 348 } 349 350 @Override 351 public void 352 rejectCall() throws CallStateException { 353 mCT.rejectCall(); 354 } 355 356 @Override 357 public void 358 switchHoldingAndActive() throws CallStateException { 359 mCT.switchWaitingOrHoldingAndActive(); 360 } 361 362 @Override 363 public boolean canConference() { 364 return mCT.canConference(); 365 } 366 367 public boolean canDial() { 368 return mCT.canDial(); 369 } 370 371 @Override 372 public void conference() { 373 mCT.conference(); 374 } 375 376 @Override 377 public void clearDisconnected() { 378 mCT.clearDisconnected(); 379 } 380 381 @Override 382 public boolean canTransfer() { 383 return mCT.canTransfer(); 384 } 385 386 @Override 387 public void explicitCallTransfer() { 388 mCT.explicitCallTransfer(); 389 } 390 391 @Override 392 public ImsPhoneCall 393 getForegroundCall() { 394 return mCT.mForegroundCall; 395 } 396 397 @Override 398 public ImsPhoneCall 399 getBackgroundCall() { 400 return mCT.mBackgroundCall; 401 } 402 403 @Override 404 public ImsPhoneCall 405 getRingingCall() { 406 return mCT.mRingingCall; 407 } 408 409 @Override 410 public boolean isImsAvailable() { 411 return mCT.isImsServiceReady(); 412 } 413 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 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 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 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 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 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 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 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 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 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 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 630 public boolean isInEcm() { 631 return mDefaultPhone.isInEcm(); 632 } 633 634 @Override 635 public void setIsInEcm(boolean isInEcm){ 636 mDefaultPhone.setIsInEcm(isInEcm); 637 } 638 639 public void notifyNewRingingConnection(Connection c) { 640 mDefaultPhone.notifyNewRingingConnectionP(c); 641 } 642 643 void notifyUnknownConnection(Connection c) { 644 mDefaultPhone.notifyUnknownConnectionP(c); 645 } 646 647 @Override 648 public void notifyForVideoCapabilityChanged(boolean isVideoCapable) { 649 mIsVideoCapable = isVideoCapable; 650 mDefaultPhone.notifyForVideoCapabilityChanged(isVideoCapable); 651 } 652 653 @Override 654 public Connection dial(String dialString, DialArgs dialArgs) throws CallStateException { 655 return dialInternal(dialString, dialArgs, null); 656 } 657 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 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 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 746 stopDtmf() { 747 mCT.stopDtmf(); 748 } 749 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 757 public void setMute(boolean muted) { 758 mCT.setMute(muted); 759 } 760 761 @Override 762 public void setTTYMode(int ttyMode, Message onComplete) { 763 mCT.setTtyMode(ttyMode); 764 } 765 766 @Override 767 public void setUiTTYMode(int uiTtyMode, Message onComplete) { 768 mCT.setUiTTYMode(uiTtyMode, onComplete); 769 } 770 771 @Override 772 public boolean getMute() { 773 return mCT.getMute(); 774 } 775 776 @Override 777 public PhoneConstants.State getState() { 778 return mCT.getState(); 779 } 780 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 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 807 private boolean isCfEnable(int action) { 808 return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION); 809 } 810 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 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 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 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 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 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 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 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 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 963 public void setCallWaiting(boolean enable, Message onComplete) { 964 setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete); 965 } 966 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 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 1002 public void getCallBarring(String facility, Message onComplete) { 1003 getCallBarring(facility, onComplete, CommandsInterface.SERVICE_CLASS_NONE); 1004 } 1005 1006 public void getCallBarring(String facility, Message onComplete, int serviceClass) { 1007 getCallBarring(facility, "", onComplete, serviceClass); 1008 } 1009 1010 @Override 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 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 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 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 1069 public void sendUSSD(String ussdString, Message response) { 1070 mCT.sendUSSD(ussdString, response); 1071 } 1072 1073 @Override 1074 public void cancelUSSD() { 1075 mCT.cancelUSSD(); 1076 } 1077 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 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 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 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 1145 onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) { 1146 logd("onNetworkInitiatedUssd"); 1147 mMmiCompleteRegistrants.notifyRegistrants( 1148 new AsyncResult(null, mmi, null)); 1149 } 1150 1151 /* package */ 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 */ 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 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 1238 public void notifySrvccState(Call.SrvccState state) { 1239 mCT.notifySrvccState(state); 1240 } 1241 1242 /* package */ void 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 1252 public void registerForSilentRedial(Handler h, int what, Object obj) { 1253 mSilentRedialRegistrants.addUnique(h, what, obj); 1254 } 1255 1256 @Override 1257 public void unregisterForSilentRedial(Handler h) { 1258 mSilentRedialRegistrants.remove(h); 1259 } 1260 1261 @Override 1262 public void registerForSuppServiceNotification(Handler h, int what, Object obj) { 1263 mSsnRegistrants.addUnique(h, what, obj); 1264 } 1265 1266 @Override 1267 public void unregisterForSuppServiceNotification(Handler h) { 1268 mSsnRegistrants.remove(h); 1269 } 1270 1271 @Override 1272 public int getSubId() { 1273 return mDefaultPhone.getSubId(); 1274 } 1275 1276 @Override 1277 public int getPhoneId() { 1278 return mDefaultPhone.getPhoneId(); 1279 } 1280 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 */ 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 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 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 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 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 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 1475 public ImsEcbmStateListener getImsEcbmStateListener() { 1476 return mImsEcbmStateListener; 1477 } 1478 1479 @Override 1480 public boolean isInEmergencyCall() { 1481 return mCT.isInEmergencyCall(); 1482 } 1483 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 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 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 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 */ 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 1576 public void setOnEcbModeExitResponse(Handler h, int what, Object obj) { 1577 mEcmExitRespRegistrant = new Registrant(h, what, obj); 1578 } 1579 1580 @Override 1581 public void unsetOnEcbModeExitResponse(Handler h) { 1582 mEcmExitRespRegistrant.clear(); 1583 } 1584 1585 public void onFeatureCapabilityChanged() { 1586 mDefaultPhone.getServiceStateTracker().onImsCapabilityChanged(); 1587 } 1588 1589 @Override 1590 public boolean isVolteEnabled() { 1591 return mCT.isVolteEnabled(); 1592 } 1593 1594 @Override 1595 public boolean isWifiCallingEnabled() { 1596 return mCT.isVowifiEnabled(); 1597 } 1598 1599 @Override 1600 public boolean isVideoEnabled() { 1601 return mCT.isVideoCallEnabled(); 1602 } 1603 1604 @Override 1605 public int getImsRegistrationTech() { 1606 return mCT.getImsRegistrationTech(); 1607 } 1608 1609 @Override 1610 public Phone getDefaultPhone() { 1611 return mDefaultPhone; 1612 } 1613 1614 @Override 1615 public boolean isImsRegistered() { 1616 return mImsRegistered; 1617 } 1618 1619 public void setImsRegistered(boolean value) { 1620 mImsRegistered = value; 1621 } 1622 1623 @Override 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 */ 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. 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 1784 public boolean isUtEnabled() { 1785 return mCT.isUtEnabled(); 1786 } 1787 1788 @Override 1789 public void sendEmergencyCallStateChange(boolean callActive) { 1790 mDefaultPhone.sendEmergencyCallStateChange(callActive); 1791 } 1792 1793 @Override 1794 public void setBroadcastEmergencyCallStateChanges(boolean broadcast) { 1795 mDefaultPhone.setBroadcastEmergencyCallStateChanges(broadcast); 1796 } 1797 1798 @VisibleForTesting 1799 public PowerManager.WakeLock getWakeLock() { 1800 return mWakeLock; 1801 } 1802 1803 @Override 1804 public NetworkStats getVtDataUsage(boolean perUidStats) { 1805 return mCT.getVtDataUsage(perUidStats); 1806 } 1807 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 1821 private boolean getCurrentRoaming() { 1822 TelephonyManager tm = (TelephonyManager) mContext 1823 .getSystemService(Context.TELEPHONY_SERVICE); 1824 return tm.isNetworkRoaming(); 1825 } 1826 1827 @Override 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 1848 private void logi(String s) { 1849 Rlog.i(LOG_TAG, "[" + mPhoneId + "] " + s); 1850 } 1851 1852 private void logv(String s) { 1853 Rlog.v(LOG_TAG, "[" + mPhoneId + "] " + s); 1854 } 1855 1856 private void logd(String s) { 1857 Rlog.d(LOG_TAG, "[" + mPhoneId + "] " + s); 1858 } 1859 1860 private void loge(String s) { 1861 Rlog.e(LOG_TAG, "[" + mPhoneId + "] " + s); 1862 } 1863 } 1864