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