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