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