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