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