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