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 android.telephony.ims.ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE; 20 import static android.telephony.ims.ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_TITLE; 21 22 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC; 23 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr; 24 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC; 25 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC; 26 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH; 27 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL; 28 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO; 29 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT; 30 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BIC_ACR; 31 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE; 32 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE; 33 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE; 34 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION; 35 import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL; 36 import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL; 37 import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY; 38 import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE; 39 import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY; 40 import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL; 41 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE; 42 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE; 43 44 import android.app.Activity; 45 import android.app.Notification; 46 import android.app.NotificationManager; 47 import android.app.PendingIntent; 48 import android.compat.annotation.UnsupportedAppUsage; 49 import android.content.BroadcastReceiver; 50 import android.content.Context; 51 import android.content.Intent; 52 import android.content.SharedPreferences; 53 import android.net.Uri; 54 import android.os.AsyncResult; 55 import android.os.Build; 56 import android.os.Bundle; 57 import android.os.Handler; 58 import android.os.Message; 59 import android.os.PersistableBundle; 60 import android.os.PowerManager; 61 import android.os.PowerManager.WakeLock; 62 import android.os.Registrant; 63 import android.os.RegistrantList; 64 import android.os.ResultReceiver; 65 import android.os.UserHandle; 66 import android.preference.PreferenceManager; 67 import android.sysprop.TelephonyProperties; 68 import android.telephony.AccessNetworkConstants; 69 import android.telephony.CarrierConfigManager; 70 import android.telephony.NetworkRegistrationInfo; 71 import android.telephony.PhoneNumberUtils; 72 import android.telephony.ServiceState; 73 import android.telephony.SubscriptionManager; 74 import android.telephony.TelephonyManager; 75 import android.telephony.UssdResponse; 76 import android.telephony.emergency.EmergencyNumber; 77 import android.telephony.ims.ImsCallForwardInfo; 78 import android.telephony.ims.ImsCallProfile; 79 import android.telephony.ims.ImsReasonInfo; 80 import android.telephony.ims.ImsSsData; 81 import android.telephony.ims.ImsSsInfo; 82 import android.telephony.ims.RegistrationManager; 83 import android.telephony.ims.stub.ImsUtImplBase; 84 import android.text.TextUtils; 85 import android.util.LocalLog; 86 87 import com.android.ims.ImsEcbm; 88 import com.android.ims.ImsEcbmStateListener; 89 import com.android.ims.ImsException; 90 import com.android.ims.ImsManager; 91 import com.android.ims.ImsUtInterface; 92 import com.android.internal.annotations.VisibleForTesting; 93 import com.android.internal.telephony.Call; 94 import com.android.internal.telephony.CallFailCause; 95 import com.android.internal.telephony.CallForwardInfo; 96 import com.android.internal.telephony.CallStateException; 97 import com.android.internal.telephony.CallTracker; 98 import com.android.internal.telephony.CommandException; 99 import com.android.internal.telephony.CommandsInterface; 100 import com.android.internal.telephony.Connection; 101 import com.android.internal.telephony.GsmCdmaPhone; 102 import com.android.internal.telephony.MmiCode; 103 import com.android.internal.telephony.Phone; 104 import com.android.internal.telephony.PhoneConstants; 105 import com.android.internal.telephony.PhoneNotifier; 106 import com.android.internal.telephony.ServiceStateTracker; 107 import com.android.internal.telephony.TelephonyComponentFactory; 108 import com.android.internal.telephony.TelephonyIntents; 109 import com.android.internal.telephony.dataconnection.TransportManager; 110 import com.android.internal.telephony.emergency.EmergencyNumberTracker; 111 import com.android.internal.telephony.gsm.SuppServiceNotification; 112 import com.android.internal.telephony.metrics.ImsStats; 113 import com.android.internal.telephony.metrics.TelephonyMetrics; 114 import com.android.internal.telephony.metrics.VoiceCallSessionStats; 115 import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState; 116 import com.android.internal.telephony.uicc.IccRecords; 117 import com.android.internal.telephony.util.NotificationChannelController; 118 import com.android.internal.util.IndentingPrintWriter; 119 import com.android.telephony.Rlog; 120 121 import java.io.FileDescriptor; 122 import java.io.PrintWriter; 123 import java.util.ArrayList; 124 import java.util.List; 125 import java.util.function.Consumer; 126 127 /** 128 * {@hide} 129 */ 130 public class ImsPhone extends ImsPhoneBase { 131 private static final String LOG_TAG = "ImsPhone"; 132 private static final boolean DBG = true; 133 private static final boolean VDBG = false; // STOPSHIP if true 134 135 private static final int EVENT_SET_CALL_BARRING_DONE = EVENT_LAST + 1; 136 private static final int EVENT_GET_CALL_BARRING_DONE = EVENT_LAST + 2; 137 private static final int EVENT_SET_CALL_WAITING_DONE = EVENT_LAST + 3; 138 private static final int EVENT_GET_CALL_WAITING_DONE = EVENT_LAST + 4; 139 private static final int EVENT_SET_CLIR_DONE = EVENT_LAST + 5; 140 private static final int EVENT_GET_CLIR_DONE = EVENT_LAST + 6; 141 private static final int EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED = EVENT_LAST + 7; 142 @VisibleForTesting 143 public static final int EVENT_SERVICE_STATE_CHANGED = EVENT_LAST + 8; 144 private static final int EVENT_VOICE_CALL_ENDED = EVENT_LAST + 9; 145 private static final int EVENT_INITIATE_VOLTE_SILENT_REDIAL = EVENT_LAST + 10; 146 private static final int EVENT_GET_CLIP_DONE = EVENT_LAST + 11; 147 148 static final int RESTART_ECM_TIMER = 0; // restart Ecm timer 149 static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer 150 151 // Default Emergency Callback Mode exit timer 152 private static final long DEFAULT_ECM_EXIT_TIMER_VALUE = 300000; 153 154 // String to Call Composer Option Prefix set by user 155 private static final String PREF_USER_SET_CALL_COMPOSER_PREFIX = "userset_callcomposer_prefix"; 156 157 /** 158 * Used to create ImsManager instances, which may be injected during testing. 159 */ 160 @VisibleForTesting 161 public interface ImsManagerFactory { 162 /** 163 * Create a new instance of ImsManager for the specified phoneId. 164 */ create(Context context, int phoneId)165 ImsManager create(Context context, int phoneId); 166 } 167 168 public static class ImsDialArgs extends DialArgs { 169 public static class Builder extends DialArgs.Builder<ImsDialArgs.Builder> { 170 private android.telecom.Connection.RttTextStream mRttTextStream; 171 private int mRetryCallFailCause = ImsReasonInfo.CODE_UNSPECIFIED; 172 private int mRetryCallFailNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; 173 private boolean mIsWpsCall = false; 174 from(DialArgs dialArgs)175 public static ImsDialArgs.Builder from(DialArgs dialArgs) { 176 if (dialArgs instanceof ImsDialArgs) { 177 return new ImsDialArgs.Builder() 178 .setUusInfo(dialArgs.uusInfo) 179 .setIsEmergency(dialArgs.isEmergency) 180 .setVideoState(dialArgs.videoState) 181 .setIntentExtras(dialArgs.intentExtras) 182 .setRttTextStream(((ImsDialArgs)dialArgs).rttTextStream) 183 .setClirMode(dialArgs.clirMode) 184 .setRetryCallFailCause(((ImsDialArgs)dialArgs).retryCallFailCause) 185 .setRetryCallFailNetworkType( 186 ((ImsDialArgs)dialArgs).retryCallFailNetworkType) 187 .setIsWpsCall(((ImsDialArgs)dialArgs).isWpsCall); 188 } 189 return new ImsDialArgs.Builder() 190 .setUusInfo(dialArgs.uusInfo) 191 .setIsEmergency(dialArgs.isEmergency) 192 .setVideoState(dialArgs.videoState) 193 .setClirMode(dialArgs.clirMode) 194 .setIntentExtras(dialArgs.intentExtras); 195 } 196 setRttTextStream( android.telecom.Connection.RttTextStream s)197 public ImsDialArgs.Builder setRttTextStream( 198 android.telecom.Connection.RttTextStream s) { 199 mRttTextStream = s; 200 return this; 201 } 202 setRetryCallFailCause(int retryCallFailCause)203 public ImsDialArgs.Builder setRetryCallFailCause(int retryCallFailCause) { 204 this.mRetryCallFailCause = retryCallFailCause; 205 return this; 206 } 207 setRetryCallFailNetworkType(int retryCallFailNetworkType)208 public ImsDialArgs.Builder setRetryCallFailNetworkType(int retryCallFailNetworkType) { 209 this.mRetryCallFailNetworkType = retryCallFailNetworkType; 210 return this; 211 } 212 setIsWpsCall(boolean isWpsCall)213 public ImsDialArgs.Builder setIsWpsCall(boolean isWpsCall) { 214 this.mIsWpsCall = isWpsCall; 215 return this; 216 } 217 build()218 public ImsDialArgs build() { 219 return new ImsDialArgs(this); 220 } 221 } 222 223 /** 224 * The RTT text stream. If non-null, indicates that connection supports RTT 225 * communication with the in-call app. 226 */ 227 public final android.telecom.Connection.RttTextStream rttTextStream; 228 229 public final int retryCallFailCause; 230 public final int retryCallFailNetworkType; 231 232 /** Indicates the call is Wireless Priority Service call */ 233 public final boolean isWpsCall; 234 ImsDialArgs(ImsDialArgs.Builder b)235 private ImsDialArgs(ImsDialArgs.Builder b) { 236 super(b); 237 this.rttTextStream = b.mRttTextStream; 238 this.retryCallFailCause = b.mRetryCallFailCause; 239 this.retryCallFailNetworkType = b.mRetryCallFailNetworkType; 240 this.isWpsCall = b.mIsWpsCall; 241 } 242 } 243 244 // Instance Variables 245 Phone mDefaultPhone; 246 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 247 ImsPhoneCallTracker mCT; 248 ImsExternalCallTracker mExternalCallTracker; 249 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 250 private ArrayList <ImsPhoneMmiCode> mPendingMMIs = new ArrayList<ImsPhoneMmiCode>(); 251 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 252 private ServiceState mSS = new ServiceState(); 253 254 private final ImsManagerFactory mImsManagerFactory; 255 256 private SharedPreferences mImsPhoneSharedPreferences; 257 258 // To redial silently through GSM or CDMA when dialing through IMS fails 259 private String mLastDialString; 260 261 private WakeLock mWakeLock; 262 263 // mEcmExitRespRegistrant is informed after the phone has been exited the emergency 264 // callback mode keep track of if phone is in emergency callback mode 265 private Registrant mEcmExitRespRegistrant; 266 267 private final RegistrantList mSilentRedialRegistrants = new RegistrantList(); 268 269 private final LocalLog mRegLocalLog = new LocalLog(100); 270 private TelephonyMetrics mMetrics; 271 272 // The helper class to receive and store the MmTel registration status updated. 273 private ImsRegistrationCallbackHelper mImsMmTelRegistrationHelper; 274 275 // The roaming state if currently in service, or the last roaming state when was in service. 276 private boolean mLastKnownRoamingState = false; 277 278 private boolean mIsInImsEcm = false; 279 280 // List of Registrants to send supplementary service notifications to. 281 private RegistrantList mSsnRegistrants = new RegistrantList(); 282 283 private ImsStats mImsStats; 284 285 // A runnable which is used to automatically exit from Ecm after a period of time. 286 private Runnable mExitEcmRunnable = new Runnable() { 287 @Override 288 public void run() { 289 exitEmergencyCallbackMode(); 290 } 291 }; 292 293 private Uri[] mCurrentSubscriberUris; 294 setCurrentSubscriberUris(Uri[] currentSubscriberUris)295 protected void setCurrentSubscriberUris(Uri[] currentSubscriberUris) { 296 this.mCurrentSubscriberUris = currentSubscriberUris; 297 } 298 299 @Override getCurrentSubscriberUris()300 public Uri[] getCurrentSubscriberUris() { 301 return mCurrentSubscriberUris; 302 } 303 304 /** Set call composer status from users for the current subscription */ setCallComposerStatus(int status)305 public void setCallComposerStatus(int status) { 306 mImsPhoneSharedPreferences.edit().putInt( 307 PREF_USER_SET_CALL_COMPOSER_PREFIX + getSubId(), status).commit(); 308 } 309 310 /** Get call composer status from users for the current subscription */ getCallComposerStatus()311 public int getCallComposerStatus() { 312 return mImsPhoneSharedPreferences.getInt(PREF_USER_SET_CALL_COMPOSER_PREFIX + getSubId(), 313 TelephonyManager.CALL_COMPOSER_STATUS_OFF); 314 } 315 316 @Override getEmergencyNumberDbVersion()317 public int getEmergencyNumberDbVersion() { 318 return getEmergencyNumberTracker().getEmergencyNumberDbVersion(); 319 } 320 321 @Override getEmergencyNumberTracker()322 public EmergencyNumberTracker getEmergencyNumberTracker() { 323 return mDefaultPhone.getEmergencyNumberTracker(); 324 } 325 326 @Override getServiceStateTracker()327 public ServiceStateTracker getServiceStateTracker() { 328 return mDefaultPhone.getServiceStateTracker(); 329 } 330 331 // Create Cf (Call forward) so that dialling number & 332 // mIsCfu (true if reason is call forward unconditional) 333 // mOnComplete (Message object passed by client) can be packed & 334 // given as a single Cf object as user data to UtInterface. 335 private static class Cf { 336 final String mSetCfNumber; 337 final Message mOnComplete; 338 final boolean mIsCfu; 339 340 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) Cf(String cfNumber, boolean isCfu, Message onComplete)341 Cf(String cfNumber, boolean isCfu, Message onComplete) { 342 mSetCfNumber = cfNumber; 343 mIsCfu = isCfu; 344 mOnComplete = onComplete; 345 } 346 } 347 348 // Create SS (Supplementary Service) so that save SS params & 349 // mOnComplete (Message object passed by client) can be packed 350 // given as a single SS object as user data to UtInterface. 351 @VisibleForTesting 352 public static class SS { 353 int mCfAction; 354 int mCfReason; 355 String mDialingNumber; 356 int mTimerSeconds; 357 boolean mEnable; 358 int mClirMode; 359 String mFacility; 360 boolean mLockState; 361 String mPassword; 362 int mServiceClass; 363 @VisibleForTesting 364 public Message mOnComplete; 365 366 // Default // Query CW, CLIR, CLIP SS(Message onComplete)367 SS(Message onComplete) { 368 mOnComplete = onComplete; 369 } 370 371 // Update CLIP SS(boolean enable, Message onComplete)372 SS(boolean enable, Message onComplete) { 373 mEnable = enable; 374 mOnComplete = onComplete; 375 } 376 377 // Update CLIR SS(int clirMode, Message onComplete)378 SS(int clirMode, Message onComplete) { 379 mClirMode = clirMode; 380 mOnComplete = onComplete; 381 } 382 383 // Update CW SS(boolean enable, int serviceClass, Message onComplete)384 SS(boolean enable, int serviceClass, Message onComplete) { 385 mEnable = enable; 386 mServiceClass = serviceClass; 387 mOnComplete = onComplete; 388 } 389 390 // Query CF SS(int cfReason, int serviceClass, Message onComplete)391 SS(int cfReason, int serviceClass, Message onComplete) { 392 mCfReason = cfReason; 393 mServiceClass = serviceClass; 394 mOnComplete = onComplete; 395 } 396 397 // Update CF SS(int cfAction, int cfReason, String dialingNumber, int serviceClass, int timerSeconds, Message onComplete)398 SS(int cfAction, int cfReason, String dialingNumber, 399 int serviceClass, int timerSeconds, Message onComplete) { 400 mCfAction = cfAction; 401 mCfReason = cfReason; 402 mDialingNumber = dialingNumber; 403 mServiceClass = serviceClass; 404 mTimerSeconds = timerSeconds; 405 mOnComplete = onComplete; 406 } 407 408 // Query CB SS(String facility, String password, int serviceClass, Message onComplete)409 SS(String facility, String password, int serviceClass, Message onComplete) { 410 mFacility = facility; 411 mPassword = password; 412 mServiceClass = serviceClass; 413 mOnComplete = onComplete; 414 } 415 416 // Update CB SS(String facility, boolean lockState, String password, int serviceClass, Message onComplete)417 SS(String facility, boolean lockState, String password, 418 int serviceClass, Message onComplete) { 419 mFacility = facility; 420 mLockState = lockState; 421 mPassword = password; 422 mServiceClass = serviceClass; 423 mOnComplete = onComplete; 424 } 425 } 426 427 // Constructors ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone)428 public ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone) { 429 this(context, notifier, defaultPhone, ImsManager::getInstance, false); 430 } 431 432 @VisibleForTesting ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone, ImsManagerFactory imsManagerFactory, boolean unitTestMode)433 public ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone, 434 ImsManagerFactory imsManagerFactory, boolean unitTestMode) { 435 super("ImsPhone", context, notifier, unitTestMode); 436 437 mDefaultPhone = defaultPhone; 438 mImsManagerFactory = imsManagerFactory; 439 mImsPhoneSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); 440 mImsStats = new ImsStats(this); 441 // The ImsExternalCallTracker needs to be defined before the ImsPhoneCallTracker, as the 442 // ImsPhoneCallTracker uses a thread to spool up the ImsManager. Part of this involves 443 // setting the multiendpoint listener on the external call tracker. So we need to ensure 444 // the external call tracker is available first to avoid potential timing issues. 445 mExternalCallTracker = 446 TelephonyComponentFactory.getInstance() 447 .inject(ImsExternalCallTracker.class.getName()) 448 .makeImsExternalCallTracker(this); 449 mCT = TelephonyComponentFactory.getInstance().inject(ImsPhoneCallTracker.class.getName()) 450 .makeImsPhoneCallTracker(this); 451 mCT.registerPhoneStateListener(mExternalCallTracker); 452 mExternalCallTracker.setCallPuller(mCT); 453 454 mSS.setStateOff(); 455 456 mPhoneId = mDefaultPhone.getPhoneId(); 457 458 mMetrics = TelephonyMetrics.getInstance(); 459 460 mImsMmTelRegistrationHelper = new ImsRegistrationCallbackHelper(mMmTelRegistrationUpdate, 461 context.getMainExecutor()); 462 463 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 464 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); 465 mWakeLock.setReferenceCounted(false); 466 467 if (mDefaultPhone.getServiceStateTracker() != null 468 && mDefaultPhone.getTransportManager() != null) { 469 for (int transport : mDefaultPhone.getTransportManager().getAvailableTransports()) { 470 mDefaultPhone.getServiceStateTracker() 471 .registerForDataRegStateOrRatChanged(transport, this, 472 EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED, null); 473 } 474 } 475 // Sets the Voice reg state to STATE_OUT_OF_SERVICE and also queries the data service 476 // state. We don't ever need the voice reg state to be anything other than in or out of 477 // service. 478 setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 479 480 mDefaultPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null); 481 // Force initial roaming state update later, on EVENT_CARRIER_CONFIG_CHANGED. 482 // Settings provider or CarrierConfig may not be loaded now. 483 484 mDefaultPhone.registerForVolteSilentRedial(this, EVENT_INITIATE_VOLTE_SILENT_REDIAL, null); 485 } 486 487 //todo: get rid of this function. It is not needed since parentPhone obj never changes 488 @Override dispose()489 public void dispose() { 490 logd("dispose"); 491 // Nothing to dispose in Phone 492 //super.dispose(); 493 mPendingMMIs.clear(); 494 mExternalCallTracker.tearDown(); 495 mCT.unregisterPhoneStateListener(mExternalCallTracker); 496 mCT.unregisterForVoiceCallEnded(this); 497 mCT.dispose(); 498 499 //Force all referenced classes to unregister their former registered events 500 if (mDefaultPhone != null && mDefaultPhone.getServiceStateTracker() != null) { 501 for (int transport : mDefaultPhone.getTransportManager().getAvailableTransports()) { 502 mDefaultPhone.getServiceStateTracker() 503 .unregisterForDataRegStateOrRatChanged(transport, this); 504 } 505 mDefaultPhone.unregisterForServiceStateChanged(this); 506 } 507 508 if (mDefaultPhone != null) { 509 mDefaultPhone.unregisterForVolteSilentRedial(this); 510 } 511 } 512 513 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 514 @Override getServiceState()515 public ServiceState getServiceState() { 516 return mSS; 517 } 518 519 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 520 @VisibleForTesting setServiceState(int state)521 public void setServiceState(int state) { 522 boolean isVoiceRegStateChanged = false; 523 524 synchronized (this) { 525 isVoiceRegStateChanged = mSS.getState() != state; 526 mSS.setVoiceRegState(state); 527 } 528 updateDataServiceState(); 529 530 if (isVoiceRegStateChanged) { 531 if (mDefaultPhone.getServiceStateTracker() != null) { 532 mDefaultPhone.getServiceStateTracker().onImsServiceStateChanged(); 533 } 534 } 535 } 536 537 @Override getCallTracker()538 public CallTracker getCallTracker() { 539 return mCT; 540 } 541 getExternalCallTracker()542 public ImsExternalCallTracker getExternalCallTracker() { 543 return mExternalCallTracker; 544 } 545 546 @Override 547 public List<? extends ImsPhoneMmiCode> getPendingMmiCodes()548 getPendingMmiCodes() { 549 return mPendingMMIs; 550 } 551 552 @Override 553 public void acceptCall(int videoState)554 acceptCall(int videoState) throws CallStateException { 555 mCT.acceptCall(videoState); 556 } 557 558 @Override 559 public void rejectCall()560 rejectCall() throws CallStateException { 561 mCT.rejectCall(); 562 } 563 564 @Override 565 public void switchHoldingAndActive()566 switchHoldingAndActive() throws CallStateException { 567 throw new UnsupportedOperationException("Use hold() and unhold() instead."); 568 } 569 570 @Override canConference()571 public boolean canConference() { 572 return mCT.canConference(); 573 } 574 canDial()575 public boolean canDial() { 576 try { 577 mCT.checkForDialIssues(); 578 } catch (CallStateException cse) { 579 return false; 580 } 581 return true; 582 } 583 584 @Override conference()585 public void conference() { 586 mCT.conference(); 587 } 588 589 @Override clearDisconnected()590 public void clearDisconnected() { 591 mCT.clearDisconnected(); 592 } 593 594 @Override canTransfer()595 public boolean canTransfer() { 596 return mCT.canTransfer(); 597 } 598 599 @Override explicitCallTransfer()600 public void explicitCallTransfer() throws CallStateException { 601 mCT.explicitCallTransfer(); 602 } 603 604 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 605 @Override 606 public ImsPhoneCall getForegroundCall()607 getForegroundCall() { 608 return mCT.mForegroundCall; 609 } 610 611 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 612 @Override 613 public ImsPhoneCall getBackgroundCall()614 getBackgroundCall() { 615 return mCT.mBackgroundCall; 616 } 617 618 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 619 @Override 620 public ImsPhoneCall getRingingCall()621 getRingingCall() { 622 return mCT.mRingingCall; 623 } 624 625 @Override isImsAvailable()626 public boolean isImsAvailable() { 627 return mCT.isImsServiceReady(); 628 } 629 630 /** 631 * Hold the currently active call, possibly unholding a currently held call. 632 * @throws CallStateException 633 */ holdActiveCall()634 public void holdActiveCall() throws CallStateException { 635 mCT.holdActiveCall(); 636 } 637 638 /** 639 * Unhold the currently active call, possibly holding a currently active call. 640 * If the call tracker is already in the middle of a hold operation, this is a noop. 641 * @throws CallStateException 642 */ unholdHeldCall()643 public void unholdHeldCall() throws CallStateException { 644 mCT.unholdHeldCall(); 645 } 646 handleCallDeflectionIncallSupplementaryService( String dialString)647 private boolean handleCallDeflectionIncallSupplementaryService( 648 String dialString) { 649 if (dialString.length() > 1) { 650 return false; 651 } 652 653 if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) { 654 if (DBG) logd("MmiCode 0: rejectCall"); 655 try { 656 mCT.rejectCall(); 657 } catch (CallStateException e) { 658 if (DBG) Rlog.d(LOG_TAG, "reject failed", e); 659 notifySuppServiceFailed(Phone.SuppService.REJECT); 660 } 661 } else if (getBackgroundCall().getState() != ImsPhoneCall.State.IDLE) { 662 if (DBG) logd("MmiCode 0: hangupWaitingOrBackground"); 663 try { 664 mCT.hangup(getBackgroundCall()); 665 } catch (CallStateException e) { 666 if (DBG) Rlog.d(LOG_TAG, "hangup failed", e); 667 } 668 } 669 670 return true; 671 } 672 sendUssdResponse(String ussdRequest, CharSequence message, int returnCode, ResultReceiver wrappedCallback)673 private void sendUssdResponse(String ussdRequest, CharSequence message, int returnCode, 674 ResultReceiver wrappedCallback) { 675 UssdResponse response = new UssdResponse(ussdRequest, message); 676 Bundle returnData = new Bundle(); 677 returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response); 678 wrappedCallback.send(returnCode, returnData); 679 680 } 681 682 @Override handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback)683 public boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback) 684 throws CallStateException { 685 if (mPendingMMIs.size() > 0) { 686 // There are MMI codes in progress; fail attempt now. 687 logi("handleUssdRequest: queue full: " + Rlog.pii(LOG_TAG, ussdRequest)); 688 sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE, 689 wrappedCallback ); 690 return true; 691 } 692 try { 693 dialInternal(ussdRequest, new ImsDialArgs.Builder().build(), wrappedCallback); 694 } catch (CallStateException cse) { 695 if (CS_FALLBACK.equals(cse.getMessage())) { 696 throw cse; 697 } else { 698 Rlog.w(LOG_TAG, "Could not execute USSD " + cse); 699 sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE, 700 wrappedCallback); 701 } 702 } catch (Exception e) { 703 Rlog.w(LOG_TAG, "Could not execute USSD " + e); 704 sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE, 705 wrappedCallback); 706 return false; 707 } 708 return true; 709 } 710 handleCallWaitingIncallSupplementaryService( String dialString)711 private boolean handleCallWaitingIncallSupplementaryService( 712 String dialString) { 713 int len = dialString.length(); 714 715 if (len > 2) { 716 return false; 717 } 718 719 ImsPhoneCall call = getForegroundCall(); 720 721 try { 722 if (len > 1) { 723 if (DBG) logd("not support 1X SEND"); 724 notifySuppServiceFailed(Phone.SuppService.HANGUP); 725 } else { 726 if (call.getState() != ImsPhoneCall.State.IDLE) { 727 if (DBG) logd("MmiCode 1: hangup foreground"); 728 mCT.hangup(call); 729 } else { 730 if (DBG) logd("MmiCode 1: holdActiveCallForWaitingCall"); 731 mCT.holdActiveCallForWaitingCall(); 732 } 733 } 734 } catch (CallStateException e) { 735 if (DBG) Rlog.d(LOG_TAG, "hangup failed", e); 736 notifySuppServiceFailed(Phone.SuppService.HANGUP); 737 } 738 739 return true; 740 } 741 handleCallHoldIncallSupplementaryService(String dialString)742 private boolean handleCallHoldIncallSupplementaryService(String dialString) { 743 int len = dialString.length(); 744 745 if (len > 2) { 746 return false; 747 } 748 749 if (len > 1) { 750 if (DBG) logd("separate not supported"); 751 notifySuppServiceFailed(Phone.SuppService.SEPARATE); 752 } else { 753 try { 754 if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) { 755 if (DBG) logd("MmiCode 2: accept ringing call"); 756 mCT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE); 757 } else if (getBackgroundCall().getState() == ImsPhoneCall.State.HOLDING) { 758 // If there's an active ongoing call as well, hold it and the background one 759 // should automatically unhold. Otherwise just unhold the background call. 760 if (getForegroundCall().getState() != ImsPhoneCall.State.IDLE) { 761 if (DBG) logd("MmiCode 2: switch holding and active"); 762 mCT.holdActiveCall(); 763 } else { 764 if (DBG) logd("MmiCode 2: unhold held call"); 765 mCT.unholdHeldCall(); 766 } 767 } else if (getForegroundCall().getState() != ImsPhoneCall.State.IDLE) { 768 if (DBG) logd("MmiCode 2: hold active call"); 769 mCT.holdActiveCall(); 770 } 771 } catch (CallStateException e) { 772 if (DBG) Rlog.d(LOG_TAG, "switch failed", e); 773 notifySuppServiceFailed(Phone.SuppService.SWITCH); 774 } 775 } 776 777 return true; 778 } 779 handleMultipartyIncallSupplementaryService( String dialString)780 private boolean handleMultipartyIncallSupplementaryService( 781 String dialString) { 782 if (dialString.length() > 1) { 783 return false; 784 } 785 786 if (DBG) logd("MmiCode 3: merge calls"); 787 conference(); 788 return true; 789 } 790 handleEctIncallSupplementaryService(String dialString)791 private boolean handleEctIncallSupplementaryService(String dialString) { 792 if (dialString.length() != 1) { 793 return false; 794 } 795 796 if (DBG) logd("MmiCode 4: explicit call transfer"); 797 try { 798 explicitCallTransfer(); 799 } catch (CallStateException e) { 800 if (DBG) Rlog.d(LOG_TAG, "explicit call transfer failed", e); 801 notifySuppServiceFailed(Phone.SuppService.TRANSFER); 802 } 803 return true; 804 } 805 handleCcbsIncallSupplementaryService(String dialString)806 private boolean handleCcbsIncallSupplementaryService(String dialString) { 807 if (dialString.length() > 1) { 808 return false; 809 } 810 811 logi("MmiCode 5: CCBS not supported!"); 812 // Treat it as an "unknown" service. 813 notifySuppServiceFailed(Phone.SuppService.UNKNOWN); 814 return true; 815 } 816 notifySuppSvcNotification(SuppServiceNotification suppSvc)817 public void notifySuppSvcNotification(SuppServiceNotification suppSvc) { 818 logd("notifySuppSvcNotification: suppSvc = " + suppSvc); 819 820 AsyncResult ar = new AsyncResult(null, suppSvc, null); 821 mSsnRegistrants.notifyRegistrants(ar); 822 } 823 824 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 825 @Override handleInCallMmiCommands(String dialString)826 public boolean handleInCallMmiCommands(String dialString) { 827 if (!isInCall()) { 828 return false; 829 } 830 831 if (TextUtils.isEmpty(dialString)) { 832 return false; 833 } 834 835 boolean result = false; 836 char ch = dialString.charAt(0); 837 switch (ch) { 838 case '0': 839 result = handleCallDeflectionIncallSupplementaryService( 840 dialString); 841 break; 842 case '1': 843 result = handleCallWaitingIncallSupplementaryService( 844 dialString); 845 break; 846 case '2': 847 result = handleCallHoldIncallSupplementaryService(dialString); 848 break; 849 case '3': 850 result = handleMultipartyIncallSupplementaryService(dialString); 851 break; 852 case '4': 853 result = handleEctIncallSupplementaryService(dialString); 854 break; 855 case '5': 856 result = handleCcbsIncallSupplementaryService(dialString); 857 break; 858 default: 859 break; 860 } 861 862 return result; 863 } 864 isInCall()865 boolean isInCall() { 866 ImsPhoneCall.State foregroundCallState = getForegroundCall().getState(); 867 ImsPhoneCall.State backgroundCallState = getBackgroundCall().getState(); 868 ImsPhoneCall.State ringingCallState = getRingingCall().getState(); 869 870 return (foregroundCallState.isAlive() || 871 backgroundCallState.isAlive() || 872 ringingCallState.isAlive()); 873 } 874 875 @Override isInImsEcm()876 public boolean isInImsEcm() { 877 return mIsInImsEcm; 878 } 879 880 @Override isInEcm()881 public boolean isInEcm() { 882 return mDefaultPhone.isInEcm(); 883 } 884 885 @Override setIsInEcm(boolean isInEcm)886 public void setIsInEcm(boolean isInEcm){ 887 mIsInImsEcm = isInEcm; 888 mDefaultPhone.setIsInEcm(isInEcm); 889 } 890 notifyNewRingingConnection(Connection c)891 public void notifyNewRingingConnection(Connection c) { 892 mDefaultPhone.notifyNewRingingConnectionP(c); 893 } 894 895 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) notifyUnknownConnection(Connection c)896 void notifyUnknownConnection(Connection c) { 897 mDefaultPhone.notifyUnknownConnectionP(c); 898 } 899 900 @Override notifyForVideoCapabilityChanged(boolean isVideoCapable)901 public void notifyForVideoCapabilityChanged(boolean isVideoCapable) { 902 mIsVideoCapable = isVideoCapable; 903 mDefaultPhone.notifyForVideoCapabilityChanged(isVideoCapable); 904 } 905 906 @Override setRadioPower(boolean on, boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall, boolean forceApply)907 public void setRadioPower(boolean on, boolean forEmergencyCall, 908 boolean isSelectedPhoneForEmergencyCall, boolean forceApply) { 909 mDefaultPhone.setRadioPower(on, forEmergencyCall, isSelectedPhoneForEmergencyCall, 910 forceApply); 911 } 912 913 @Override startConference(String[] participantsToDial, DialArgs dialArgs)914 public Connection startConference(String[] participantsToDial, DialArgs dialArgs) 915 throws CallStateException { 916 ImsDialArgs.Builder imsDialArgsBuilder; 917 imsDialArgsBuilder = ImsDialArgs.Builder.from(dialArgs); 918 return mCT.startConference(participantsToDial, imsDialArgsBuilder.build()); 919 } 920 921 @Override dial(String dialString, DialArgs dialArgs, Consumer<Phone> chosenPhoneConsumer)922 public Connection dial(String dialString, DialArgs dialArgs, 923 Consumer<Phone> chosenPhoneConsumer) throws CallStateException { 924 chosenPhoneConsumer.accept(this); 925 return dialInternal(dialString, dialArgs, null); 926 } 927 dialInternal(String dialString, DialArgs dialArgs, ResultReceiver wrappedCallback)928 private Connection dialInternal(String dialString, DialArgs dialArgs, 929 ResultReceiver wrappedCallback) 930 throws CallStateException { 931 932 mLastDialString = dialString; 933 934 // Need to make sure dialString gets parsed properly 935 String newDialString = PhoneNumberUtils.stripSeparators(dialString); 936 937 // handle in-call MMI first if applicable 938 if (handleInCallMmiCommands(newDialString)) { 939 return null; 940 } 941 942 ImsDialArgs.Builder imsDialArgsBuilder; 943 imsDialArgsBuilder = ImsDialArgs.Builder.from(dialArgs); 944 // Get the CLIR info if needed 945 imsDialArgsBuilder.setClirMode(mCT.getClirMode()); 946 947 if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { 948 return mCT.dial(dialString, imsDialArgsBuilder.build()); 949 } 950 951 // Only look at the Network portion for mmi 952 String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString); 953 ImsPhoneMmiCode mmi = 954 ImsPhoneMmiCode.newFromDialString(networkPortion, this, wrappedCallback); 955 if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'..."); 956 957 if (mmi == null) { 958 return mCT.dial(dialString, imsDialArgsBuilder.build()); 959 } else if (mmi.isTemporaryModeCLIR()) { 960 imsDialArgsBuilder.setClirMode(mmi.getCLIRMode()); 961 return mCT.dial(mmi.getDialingNumber(), imsDialArgsBuilder.build()); 962 } else if (!mmi.isSupportedOverImsPhone()) { 963 // If the mmi is not supported by IMS service, 964 // try to initiate dialing with default phone 965 // Note: This code is never reached; there is a bug in isSupportedOverImsPhone which 966 // causes it to return true even though the "processCode" method ultimately throws the 967 // exception. 968 logi("dialInternal: USSD not supported by IMS; fallback to CS."); 969 throw new CallStateException(CS_FALLBACK); 970 } else { 971 mPendingMMIs.add(mmi); 972 mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); 973 974 try { 975 mmi.processCode(); 976 } catch (CallStateException cse) { 977 if (CS_FALLBACK.equals(cse.getMessage())) { 978 logi("dialInternal: fallback to GSM required."); 979 // Make sure we remove from the list of pending MMIs since it will handover to 980 // GSM. 981 mPendingMMIs.remove(mmi); 982 throw cse; 983 } 984 } 985 986 return null; 987 } 988 } 989 990 @Override 991 public void sendDtmf(char c)992 sendDtmf(char c) { 993 if (!PhoneNumberUtils.is12Key(c)) { 994 loge("sendDtmf called with invalid character '" + c + "'"); 995 } else { 996 if (mCT.getState() == PhoneConstants.State.OFFHOOK) { 997 mCT.sendDtmf(c, null); 998 } 999 } 1000 } 1001 1002 @Override 1003 public void startDtmf(char c)1004 startDtmf(char c) { 1005 if (!(PhoneNumberUtils.is12Key(c) || (c >= 'A' && c <= 'D'))) { 1006 loge("startDtmf called with invalid character '" + c + "'"); 1007 } else { 1008 mCT.startDtmf(c); 1009 } 1010 } 1011 1012 @Override 1013 public void stopDtmf()1014 stopDtmf() { 1015 mCT.stopDtmf(); 1016 } 1017 notifyIncomingRing()1018 public void notifyIncomingRing() { 1019 if (DBG) logd("notifyIncomingRing"); 1020 AsyncResult ar = new AsyncResult(null, null, null); 1021 sendMessage(obtainMessage(EVENT_CALL_RING, ar)); 1022 } 1023 1024 @Override setMute(boolean muted)1025 public void setMute(boolean muted) { 1026 mCT.setMute(muted); 1027 } 1028 1029 @Override setTTYMode(int ttyMode, Message onComplete)1030 public void setTTYMode(int ttyMode, Message onComplete) { 1031 mCT.setTtyMode(ttyMode); 1032 } 1033 1034 @Override setUiTTYMode(int uiTtyMode, Message onComplete)1035 public void setUiTTYMode(int uiTtyMode, Message onComplete) { 1036 mCT.setUiTTYMode(uiTtyMode, onComplete); 1037 } 1038 1039 @Override getMute()1040 public boolean getMute() { 1041 return mCT.getMute(); 1042 } 1043 1044 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1045 @Override getState()1046 public PhoneConstants.State getState() { 1047 return mCT.getState(); 1048 } 1049 1050 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isValidCommandInterfaceCFReason(int commandInterfaceCFReason)1051 private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) { 1052 switch (commandInterfaceCFReason) { 1053 case CF_REASON_UNCONDITIONAL: 1054 case CF_REASON_BUSY: 1055 case CF_REASON_NO_REPLY: 1056 case CF_REASON_NOT_REACHABLE: 1057 case CF_REASON_ALL: 1058 case CF_REASON_ALL_CONDITIONAL: 1059 return true; 1060 default: 1061 return false; 1062 } 1063 } 1064 1065 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isValidCommandInterfaceCFAction(int commandInterfaceCFAction)1066 private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) { 1067 switch (commandInterfaceCFAction) { 1068 case CF_ACTION_DISABLE: 1069 case CF_ACTION_ENABLE: 1070 case CF_ACTION_REGISTRATION: 1071 case CF_ACTION_ERASURE: 1072 return true; 1073 default: 1074 return false; 1075 } 1076 } 1077 1078 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isCfEnable(int action)1079 private boolean isCfEnable(int action) { 1080 return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION); 1081 } 1082 1083 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getConditionFromCFReason(int reason)1084 private int getConditionFromCFReason(int reason) { 1085 switch(reason) { 1086 case CF_REASON_UNCONDITIONAL: return ImsUtInterface.CDIV_CF_UNCONDITIONAL; 1087 case CF_REASON_BUSY: return ImsUtInterface.CDIV_CF_BUSY; 1088 case CF_REASON_NO_REPLY: return ImsUtInterface.CDIV_CF_NO_REPLY; 1089 case CF_REASON_NOT_REACHABLE: return ImsUtInterface.CDIV_CF_NOT_REACHABLE; 1090 case CF_REASON_ALL: return ImsUtInterface.CDIV_CF_ALL; 1091 case CF_REASON_ALL_CONDITIONAL: return ImsUtInterface.CDIV_CF_ALL_CONDITIONAL; 1092 default: 1093 break; 1094 } 1095 1096 return ImsUtInterface.INVALID; 1097 } 1098 getCFReasonFromCondition(int condition)1099 private int getCFReasonFromCondition(int condition) { 1100 switch(condition) { 1101 case ImsUtInterface.CDIV_CF_UNCONDITIONAL: return CF_REASON_UNCONDITIONAL; 1102 case ImsUtInterface.CDIV_CF_BUSY: return CF_REASON_BUSY; 1103 case ImsUtInterface.CDIV_CF_NO_REPLY: return CF_REASON_NO_REPLY; 1104 case ImsUtInterface.CDIV_CF_NOT_REACHABLE: return CF_REASON_NOT_REACHABLE; 1105 case ImsUtInterface.CDIV_CF_ALL: return CF_REASON_ALL; 1106 case ImsUtInterface.CDIV_CF_ALL_CONDITIONAL: return CF_REASON_ALL_CONDITIONAL; 1107 default: 1108 break; 1109 } 1110 1111 return CF_REASON_NOT_REACHABLE; 1112 } 1113 1114 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getActionFromCFAction(int action)1115 private int getActionFromCFAction(int action) { 1116 switch(action) { 1117 case CF_ACTION_DISABLE: return ImsUtInterface.ACTION_DEACTIVATION; 1118 case CF_ACTION_ENABLE: return ImsUtInterface.ACTION_ACTIVATION; 1119 case CF_ACTION_ERASURE: return ImsUtInterface.ACTION_ERASURE; 1120 case CF_ACTION_REGISTRATION: return ImsUtInterface.ACTION_REGISTRATION; 1121 default: 1122 break; 1123 } 1124 1125 return ImsUtInterface.INVALID; 1126 } 1127 1128 @Override getOutgoingCallerIdDisplay(Message onComplete)1129 public void getOutgoingCallerIdDisplay(Message onComplete) { 1130 if (DBG) logd("getCLIR"); 1131 Message resp; 1132 SS ss = new SS(onComplete); 1133 resp = obtainMessage(EVENT_GET_CLIR_DONE, ss); 1134 1135 try { 1136 ImsUtInterface ut = mCT.getUtInterface(); 1137 ut.queryCLIR(resp); 1138 } catch (ImsException e) { 1139 sendErrorResponse(onComplete, e); 1140 } 1141 } 1142 1143 @Override setOutgoingCallerIdDisplay(int clirMode, Message onComplete)1144 public void setOutgoingCallerIdDisplay(int clirMode, Message onComplete) { 1145 if (DBG) logd("setCLIR action= " + clirMode); 1146 Message resp; 1147 // Packing CLIR value in the message. This will be required for 1148 // SharedPreference caching, if the message comes back as part of 1149 // a success response. 1150 SS ss = new SS(clirMode, onComplete); 1151 resp = obtainMessage(EVENT_SET_CLIR_DONE, ss); 1152 try { 1153 ImsUtInterface ut = mCT.getUtInterface(); 1154 ut.updateCLIR(clirMode, resp); 1155 } catch (ImsException e) { 1156 sendErrorResponse(onComplete, e); 1157 } 1158 } 1159 1160 @Override queryCLIP(Message onComplete)1161 public void queryCLIP(Message onComplete) { 1162 Message resp; 1163 SS ss = new SS(onComplete); 1164 resp = obtainMessage(EVENT_GET_CLIP_DONE, ss); 1165 1166 try { 1167 Rlog.d(LOG_TAG, "ut.queryCLIP"); 1168 ImsUtInterface ut = mCT.getUtInterface(); 1169 ut.queryCLIP(resp); 1170 } catch (ImsException e) { 1171 sendErrorResponse(onComplete, e); 1172 } 1173 } 1174 1175 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1176 @Override getCallForwardingOption(int commandInterfaceCFReason, Message onComplete)1177 public void getCallForwardingOption(int commandInterfaceCFReason, 1178 Message onComplete) { 1179 getCallForwardingOption(commandInterfaceCFReason, 1180 SERVICE_CLASS_VOICE, onComplete); 1181 } 1182 1183 @Override getCallForwardingOption(int commandInterfaceCFReason, int serviceClass, Message onComplete)1184 public void getCallForwardingOption(int commandInterfaceCFReason, int serviceClass, 1185 Message onComplete) { 1186 if (DBG) logd("getCallForwardingOption reason=" + commandInterfaceCFReason); 1187 if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) { 1188 if (DBG) logd("requesting call forwarding query."); 1189 Message resp; 1190 SS ss = new SS(commandInterfaceCFReason, serviceClass, onComplete); 1191 resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, ss); 1192 1193 try { 1194 ImsUtInterface ut = mCT.getUtInterface(); 1195 ut.queryCallForward(getConditionFromCFReason(commandInterfaceCFReason), null, resp); 1196 } catch (ImsException e) { 1197 sendErrorResponse(onComplete, e); 1198 } 1199 } else if (onComplete != null) { 1200 sendErrorResponse(onComplete); 1201 } 1202 } 1203 1204 @Override setCallForwardingOption(int commandInterfaceCFAction, int commandInterfaceCFReason, String dialingNumber, int timerSeconds, Message onComplete)1205 public void setCallForwardingOption(int commandInterfaceCFAction, 1206 int commandInterfaceCFReason, 1207 String dialingNumber, 1208 int timerSeconds, 1209 Message onComplete) { 1210 setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason, dialingNumber, 1211 CommandsInterface.SERVICE_CLASS_VOICE, timerSeconds, onComplete); 1212 } 1213 1214 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1215 @Override setCallForwardingOption(int commandInterfaceCFAction, int commandInterfaceCFReason, String dialingNumber, int serviceClass, int timerSeconds, Message onComplete)1216 public void setCallForwardingOption(int commandInterfaceCFAction, 1217 int commandInterfaceCFReason, 1218 String dialingNumber, 1219 int serviceClass, 1220 int timerSeconds, 1221 Message onComplete) { 1222 if (DBG) { 1223 logd("setCallForwardingOption action=" + commandInterfaceCFAction 1224 + ", reason=" + commandInterfaceCFReason + " serviceClass=" + serviceClass); 1225 } 1226 if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) && 1227 (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) { 1228 Message resp; 1229 SS ss = new SS(commandInterfaceCFAction, commandInterfaceCFReason, 1230 dialingNumber, serviceClass, timerSeconds, onComplete); 1231 resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE, ss); 1232 1233 try { 1234 ImsUtInterface ut = mCT.getUtInterface(); 1235 ut.updateCallForward(getActionFromCFAction(commandInterfaceCFAction), 1236 getConditionFromCFReason(commandInterfaceCFReason), 1237 dialingNumber, 1238 serviceClass, 1239 timerSeconds, 1240 resp); 1241 } catch (ImsException e) { 1242 sendErrorResponse(onComplete, e); 1243 } 1244 } else if (onComplete != null) { 1245 sendErrorResponse(onComplete); 1246 } 1247 } 1248 1249 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1250 @Override getCallWaiting(Message onComplete)1251 public void getCallWaiting(Message onComplete) { 1252 if (DBG) logd("getCallWaiting"); 1253 Message resp; 1254 SS ss = new SS(onComplete); 1255 resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, ss); 1256 1257 try { 1258 ImsUtInterface ut = mCT.getUtInterface(); 1259 ut.queryCallWaiting(resp); 1260 } catch (ImsException e) { 1261 sendErrorResponse(onComplete, e); 1262 } 1263 } 1264 1265 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1266 @Override setCallWaiting(boolean enable, Message onComplete)1267 public void setCallWaiting(boolean enable, Message onComplete) { 1268 int serviceClass = CommandsInterface.SERVICE_CLASS_VOICE; 1269 CarrierConfigManager configManager = (CarrierConfigManager) 1270 getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 1271 PersistableBundle b = configManager.getConfigForSubId(getSubId()); 1272 if (b != null) { 1273 serviceClass = b.getInt(CarrierConfigManager.KEY_CALL_WAITING_SERVICE_CLASS_INT, 1274 CommandsInterface.SERVICE_CLASS_VOICE); 1275 } 1276 setCallWaiting(enable, serviceClass, onComplete); 1277 } 1278 setCallWaiting(boolean enable, int serviceClass, Message onComplete)1279 public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) { 1280 if (DBG) logd("setCallWaiting enable=" + enable); 1281 Message resp; 1282 SS ss = new SS(enable, serviceClass, onComplete); 1283 resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, ss); 1284 1285 try { 1286 ImsUtInterface ut = mCT.getUtInterface(); 1287 ut.updateCallWaiting(enable, serviceClass, resp); 1288 } catch (ImsException e) { 1289 sendErrorResponse(onComplete, e); 1290 } 1291 } 1292 getCBTypeFromFacility(String facility)1293 private int getCBTypeFromFacility(String facility) { 1294 if (CB_FACILITY_BAOC.equals(facility)) { 1295 return ImsUtImplBase.CALL_BARRING_ALL_OUTGOING; 1296 } else if (CB_FACILITY_BAOIC.equals(facility)) { 1297 return ImsUtImplBase.CALL_BARRING_OUTGOING_INTL; 1298 } else if (CB_FACILITY_BAOICxH.equals(facility)) { 1299 return ImsUtImplBase.CALL_BARRING_OUTGOING_INTL_EXCL_HOME; 1300 } else if (CB_FACILITY_BAIC.equals(facility)) { 1301 return ImsUtImplBase.CALL_BARRING_ALL_INCOMING; 1302 } else if (CB_FACILITY_BAICr.equals(facility)) { 1303 return ImsUtImplBase.CALL_BLOCKING_INCOMING_WHEN_ROAMING; 1304 } else if (CB_FACILITY_BA_ALL.equals(facility)) { 1305 return ImsUtImplBase.CALL_BARRING_ALL; 1306 } else if (CB_FACILITY_BA_MO.equals(facility)) { 1307 return ImsUtImplBase.CALL_BARRING_OUTGOING_ALL_SERVICES; 1308 } else if (CB_FACILITY_BA_MT.equals(facility)) { 1309 return ImsUtImplBase.CALL_BARRING_INCOMING_ALL_SERVICES; 1310 } else if (CB_FACILITY_BIC_ACR.equals(facility)) { 1311 return ImsUtImplBase.CALL_BARRING_ANONYMOUS_INCOMING; 1312 } 1313 1314 return 0; 1315 } 1316 getCallBarring(String facility, Message onComplete)1317 public void getCallBarring(String facility, Message onComplete) { 1318 getCallBarring(facility, onComplete, CommandsInterface.SERVICE_CLASS_VOICE); 1319 } 1320 getCallBarring(String facility, Message onComplete, int serviceClass)1321 public void getCallBarring(String facility, Message onComplete, int serviceClass) { 1322 getCallBarring(facility, "", onComplete, serviceClass); 1323 } 1324 1325 @Override getCallBarring(String facility, String password, Message onComplete, int serviceClass)1326 public void getCallBarring(String facility, String password, Message onComplete, 1327 int serviceClass) { 1328 if (DBG) logd("getCallBarring facility=" + facility + ", serviceClass = " + serviceClass); 1329 Message resp; 1330 SS ss = new SS(facility, password, serviceClass, onComplete); 1331 resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, ss); 1332 1333 try { 1334 ImsUtInterface ut = mCT.getUtInterface(); 1335 // password is not required with Ut interface 1336 ut.queryCallBarring(getCBTypeFromFacility(facility), resp, serviceClass); 1337 } catch (ImsException e) { 1338 sendErrorResponse(onComplete, e); 1339 } 1340 } 1341 setCallBarring(String facility, boolean lockState, String password, Message onComplete)1342 public void setCallBarring(String facility, boolean lockState, String password, 1343 Message onComplete) { 1344 setCallBarring(facility, lockState, password, onComplete, 1345 CommandsInterface.SERVICE_CLASS_VOICE); 1346 } 1347 1348 @Override setCallBarring(String facility, boolean lockState, String password, Message onComplete, int serviceClass)1349 public void setCallBarring(String facility, boolean lockState, String password, 1350 Message onComplete, int serviceClass) { 1351 if (DBG) { 1352 logd("setCallBarring facility=" + facility 1353 + ", lockState=" + lockState + ", serviceClass = " + serviceClass); 1354 } 1355 Message resp; 1356 SS ss = new SS(facility, lockState, password, serviceClass, onComplete); 1357 resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, ss); 1358 1359 int action; 1360 if (lockState) { 1361 action = CommandsInterface.CF_ACTION_ENABLE; 1362 } 1363 else { 1364 action = CommandsInterface.CF_ACTION_DISABLE; 1365 } 1366 1367 try { 1368 ImsUtInterface ut = mCT.getUtInterface(); 1369 ut.updateCallBarring(getCBTypeFromFacility(facility), action, 1370 resp, null, serviceClass, password); 1371 } catch (ImsException e) { 1372 sendErrorResponse(onComplete, e); 1373 } 1374 } 1375 1376 @Override sendUssdResponse(String ussdMessge)1377 public void sendUssdResponse(String ussdMessge) { 1378 logd("sendUssdResponse"); 1379 ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessge, this); 1380 mPendingMMIs.add(mmi); 1381 mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); 1382 mmi.sendUssd(ussdMessge); 1383 } 1384 sendUSSD(String ussdString, Message response)1385 public void sendUSSD(String ussdString, Message response) { 1386 Rlog.d(LOG_TAG, "sendUssd ussdString = " + ussdString); 1387 mLastDialString = ussdString; 1388 mCT.sendUSSD(ussdString, response); 1389 } 1390 1391 @Override cancelUSSD(Message msg)1392 public void cancelUSSD(Message msg) { 1393 mCT.cancelUSSD(msg); 1394 } 1395 1396 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) sendErrorResponse(Message onComplete)1397 private void sendErrorResponse(Message onComplete) { 1398 logd("sendErrorResponse"); 1399 if (onComplete != null) { 1400 AsyncResult.forMessage(onComplete, null, 1401 new CommandException(CommandException.Error.GENERIC_FAILURE)); 1402 onComplete.sendToTarget(); 1403 } 1404 } 1405 1406 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1407 @VisibleForTesting sendErrorResponse(Message onComplete, Throwable e)1408 public void sendErrorResponse(Message onComplete, Throwable e) { 1409 logd("sendErrorResponse"); 1410 if (onComplete != null) { 1411 AsyncResult.forMessage(onComplete, null, getCommandException(e)); 1412 onComplete.sendToTarget(); 1413 } 1414 } 1415 getCommandException(int code, String errorString)1416 private CommandException getCommandException(int code, String errorString) { 1417 logd("getCommandException code= " + code + ", errorString= " + errorString); 1418 CommandException.Error error = CommandException.Error.GENERIC_FAILURE; 1419 1420 switch(code) { 1421 case ImsReasonInfo.CODE_UT_NOT_SUPPORTED: 1422 error = CommandException.Error.REQUEST_NOT_SUPPORTED; 1423 break; 1424 case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH: 1425 error = CommandException.Error.PASSWORD_INCORRECT; 1426 break; 1427 case ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE: 1428 error = CommandException.Error.RADIO_NOT_AVAILABLE; 1429 break; 1430 case ImsReasonInfo.CODE_FDN_BLOCKED: 1431 error = CommandException.Error.FDN_CHECK_FAILURE; 1432 break; 1433 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL: 1434 error = CommandException.Error.SS_MODIFIED_TO_DIAL; 1435 break; 1436 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_USSD: 1437 error = CommandException.Error.SS_MODIFIED_TO_USSD; 1438 break; 1439 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_SS: 1440 error = CommandException.Error.SS_MODIFIED_TO_SS; 1441 break; 1442 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO: 1443 error = CommandException.Error.SS_MODIFIED_TO_DIAL_VIDEO; 1444 break; 1445 default: 1446 break; 1447 } 1448 1449 return new CommandException(error, errorString); 1450 } 1451 getCommandException(Throwable e)1452 private CommandException getCommandException(Throwable e) { 1453 CommandException ex = null; 1454 1455 if (e instanceof ImsException) { 1456 ex = getCommandException(((ImsException)e).getCode(), e.getMessage()); 1457 } else { 1458 logd("getCommandException generic failure"); 1459 ex = new CommandException(CommandException.Error.GENERIC_FAILURE); 1460 } 1461 return ex; 1462 } 1463 1464 private void onNetworkInitiatedUssd(ImsPhoneMmiCode mmi)1465 onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) { 1466 logd("onNetworkInitiatedUssd"); 1467 mMmiCompleteRegistrants.notifyRegistrants( 1468 new AsyncResult(null, mmi, null)); 1469 } 1470 1471 /* package */ onIncomingUSSD(int ussdMode, String ussdMessage)1472 void onIncomingUSSD(int ussdMode, String ussdMessage) { 1473 if (DBG) logd("onIncomingUSSD ussdMode=" + ussdMode); 1474 1475 boolean isUssdError; 1476 boolean isUssdRequest; 1477 1478 isUssdRequest 1479 = (ussdMode == CommandsInterface.USSD_MODE_REQUEST); 1480 1481 isUssdError 1482 = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY 1483 && ussdMode != CommandsInterface.USSD_MODE_REQUEST); 1484 1485 ImsPhoneMmiCode found = null; 1486 for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) { 1487 if(mPendingMMIs.get(i).isPendingUSSD()) { 1488 found = mPendingMMIs.get(i); 1489 break; 1490 } 1491 } 1492 1493 if (found != null) { 1494 // Complete pending USSD 1495 if (isUssdError) { 1496 found.onUssdFinishedError(); 1497 } else { 1498 found.onUssdFinished(ussdMessage, isUssdRequest); 1499 } 1500 } else if (!isUssdError && !TextUtils.isEmpty(ussdMessage)) { 1501 // pending USSD not found 1502 // The network may initiate its own USSD request 1503 1504 // ignore everything that isnt a Notify or a Request 1505 // also, discard if there is no message to present 1506 ImsPhoneMmiCode mmi; 1507 mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage, 1508 isUssdRequest, 1509 this); 1510 onNetworkInitiatedUssd(mmi); 1511 } else if (isUssdError) { 1512 ImsPhoneMmiCode mmi; 1513 mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage, 1514 true, 1515 this); 1516 mmi.onUssdFinishedError(); 1517 } 1518 } 1519 1520 /** 1521 * Removes the given MMI from the pending list and notifies 1522 * registrants that it is complete. 1523 * @param mmi MMI that is done 1524 */ 1525 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) onMMIDone(ImsPhoneMmiCode mmi)1526 public void onMMIDone(ImsPhoneMmiCode mmi) { 1527 /* Only notify complete if it's on the pending list. 1528 * Otherwise, it's already been handled (eg, previously canceled). 1529 * The exception is cancellation of an incoming USSD-REQUEST, which is 1530 * not on the list. 1531 */ 1532 logd("onMMIDone: mmi=" + mmi); 1533 if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest() || mmi.isSsInfo()) { 1534 ResultReceiver receiverCallback = mmi.getUssdCallbackReceiver(); 1535 if (receiverCallback != null) { 1536 int returnCode = (mmi.getState() == MmiCode.State.COMPLETE) ? 1537 TelephonyManager.USSD_RETURN_SUCCESS : TelephonyManager.USSD_RETURN_FAILURE; 1538 sendUssdResponse(mmi.getDialString(), mmi.getMessage(), returnCode, 1539 receiverCallback ); 1540 } else { 1541 logv("onMMIDone: notifyRegistrants"); 1542 mMmiCompleteRegistrants.notifyRegistrants( 1543 new AsyncResult(null, mmi, null)); 1544 } 1545 } 1546 } 1547 1548 @Override getHandoverConnection()1549 public ArrayList<Connection> getHandoverConnection() { 1550 ArrayList<Connection> connList = new ArrayList<Connection>(); 1551 // Add all foreground call connections 1552 connList.addAll(getForegroundCall().getConnections()); 1553 // Add all background call connections 1554 connList.addAll(getBackgroundCall().getConnections()); 1555 // Add all background call connections 1556 connList.addAll(getRingingCall().getConnections()); 1557 if (connList.size() > 0) { 1558 return connList; 1559 } else { 1560 return null; 1561 } 1562 } 1563 1564 @Override notifySrvccState(Call.SrvccState state)1565 public void notifySrvccState(Call.SrvccState state) { 1566 mCT.notifySrvccState(state); 1567 } 1568 1569 /* package */ void initiateSilentRedial()1570 initiateSilentRedial() { 1571 initiateSilentRedial(false, EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED); 1572 } 1573 1574 /* package */ void initiateSilentRedial(boolean isEmergency, int eccCategory)1575 initiateSilentRedial(boolean isEmergency, int eccCategory) { 1576 DialArgs dialArgs = new DialArgs.Builder() 1577 .setIsEmergency(isEmergency) 1578 .setEccCategory(eccCategory) 1579 .build(); 1580 int cause = CallFailCause.LOCAL_CALL_CS_RETRY_REQUIRED; 1581 AsyncResult ar = new AsyncResult(null, 1582 new SilentRedialParam(mLastDialString, cause, dialArgs), 1583 null); 1584 if (ar != null) { 1585 // There is a race condition that can happen in some cases: 1586 // (Main thread) dial start 1587 // (Binder Thread) onCallSessionFailed 1588 // (Binder Thread) schedule a redial for CS on the main thread 1589 // (Main Thread) dial finish 1590 // (Main Thread) schedule to associate ImsPhoneConnection with 1591 // GsmConnection on the main thread 1592 // If scheduling the CS redial occurs before the command to schedule the 1593 // ImsPhoneConnection to be associated with the GsmConnection, the CS redial will occur 1594 // before GsmConnection has had callbacks to ImsPhone correctly updated. This will cause 1595 // Callbacks back to GsmCdmaPhone to never be set up correctly and we will lose track of 1596 // the instance. 1597 // Instead, schedule this redial to happen on the main thread, so that we know dial has 1598 // finished before scheduling a redial: 1599 // (Main thread) dial start 1600 // (Binder Thread) onCallSessionFailed -> move notify registrants to main thread 1601 // (Main Thread) dial finish 1602 // (Main Thread) schedule on main thread to associate ImsPhoneConnection with 1603 // GsmConnection 1604 // (Main Thread) schedule a redial for CS 1605 mContext.getMainExecutor().execute(() -> { 1606 logd("initiateSilentRedial: notifying registrants, isEmergency=" + isEmergency 1607 + ", eccCategory=" + eccCategory); 1608 mSilentRedialRegistrants.notifyRegistrants(ar); 1609 }); 1610 } 1611 } 1612 1613 @Override registerForSilentRedial(Handler h, int what, Object obj)1614 public void registerForSilentRedial(Handler h, int what, Object obj) { 1615 mSilentRedialRegistrants.addUnique(h, what, obj); 1616 } 1617 1618 @Override unregisterForSilentRedial(Handler h)1619 public void unregisterForSilentRedial(Handler h) { 1620 mSilentRedialRegistrants.remove(h); 1621 } 1622 1623 @Override registerForSuppServiceNotification(Handler h, int what, Object obj)1624 public void registerForSuppServiceNotification(Handler h, int what, Object obj) { 1625 mSsnRegistrants.addUnique(h, what, obj); 1626 } 1627 1628 @Override unregisterForSuppServiceNotification(Handler h)1629 public void unregisterForSuppServiceNotification(Handler h) { 1630 mSsnRegistrants.remove(h); 1631 } 1632 1633 @Override getSubId()1634 public int getSubId() { 1635 return mDefaultPhone.getSubId(); 1636 } 1637 1638 @Override getPhoneId()1639 public int getPhoneId() { 1640 return mDefaultPhone.getPhoneId(); 1641 } 1642 getCallForwardInfo(ImsCallForwardInfo info)1643 private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) { 1644 CallForwardInfo cfInfo = new CallForwardInfo(); 1645 cfInfo.status = info.getStatus(); 1646 cfInfo.reason = getCFReasonFromCondition(info.getCondition()); 1647 cfInfo.serviceClass = SERVICE_CLASS_VOICE; 1648 cfInfo.toa = info.getToA(); 1649 cfInfo.number = info.getNumber(); 1650 cfInfo.timeSeconds = info.getTimeSeconds(); 1651 return cfInfo; 1652 } 1653 1654 @Override getLine1Number()1655 public String getLine1Number() { 1656 return mDefaultPhone.getLine1Number(); 1657 } 1658 1659 /** 1660 * Used to Convert ImsCallForwardInfo[] to CallForwardInfo[]. 1661 * Update received call forward status to default IccRecords. 1662 */ handleCfQueryResult(ImsCallForwardInfo[] infos)1663 public CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) { 1664 CallForwardInfo[] cfInfos = null; 1665 1666 if (infos != null && infos.length != 0) { 1667 cfInfos = new CallForwardInfo[infos.length]; 1668 } 1669 1670 if (infos == null || infos.length == 0) { 1671 // Assume the default is not active 1672 // Set unconditional CFF in SIM to false 1673 setVoiceCallForwardingFlag(getIccRecords(), 1, false, null); 1674 } else { 1675 for (int i = 0, s = infos.length; i < s; i++) { 1676 if (infos[i].getCondition() == ImsUtInterface.CDIV_CF_UNCONDITIONAL) { 1677 setVoiceCallForwardingFlag(getIccRecords(), 1, (infos[i].getStatus() == 1), 1678 infos[i].getNumber()); 1679 } 1680 cfInfos[i] = getCallForwardInfo(infos[i]); 1681 } 1682 } 1683 1684 return cfInfos; 1685 } 1686 handleCbQueryResult(ImsSsInfo[] infos)1687 private int[] handleCbQueryResult(ImsSsInfo[] infos) { 1688 int[] cbInfos = new int[1]; 1689 cbInfos[0] = SERVICE_CLASS_NONE; 1690 1691 if (infos[0].getStatus() == 1) { 1692 cbInfos[0] = SERVICE_CLASS_VOICE; 1693 } 1694 1695 return cbInfos; 1696 } 1697 handleCwQueryResult(ImsSsInfo[] infos)1698 private int[] handleCwQueryResult(ImsSsInfo[] infos) { 1699 int[] cwInfos = new int[2]; 1700 cwInfos[0] = 0; 1701 1702 if (infos[0].getStatus() == 1) { 1703 cwInfos[0] = 1; 1704 cwInfos[1] = SERVICE_CLASS_VOICE; 1705 } 1706 1707 return cwInfos; 1708 } 1709 1710 private void sendResponse(Message onComplete, Object result, Throwable e)1711 sendResponse(Message onComplete, Object result, Throwable e) { 1712 if (onComplete != null) { 1713 CommandException ex = null; 1714 if (e != null) { 1715 ex = getCommandException(e); 1716 } 1717 AsyncResult.forMessage(onComplete, result, ex); 1718 onComplete.sendToTarget(); 1719 } 1720 } 1721 updateDataServiceState()1722 private void updateDataServiceState() { 1723 if (mSS != null && mDefaultPhone.getServiceStateTracker() != null 1724 && mDefaultPhone.getServiceStateTracker().mSS != null) { 1725 ServiceState ss = mDefaultPhone.getServiceStateTracker().mSS; 1726 mSS.setDataRegState(ss.getDataRegistrationState()); 1727 List<NetworkRegistrationInfo> nriList = 1728 ss.getNetworkRegistrationInfoListForDomain(NetworkRegistrationInfo.DOMAIN_PS); 1729 for (NetworkRegistrationInfo nri : nriList) { 1730 mSS.addNetworkRegistrationInfo(nri); 1731 } 1732 1733 mSS.setIwlanPreferred(ss.isIwlanPreferred()); 1734 logd("updateDataServiceState: defSs = " + ss + " imsSs = " + mSS); 1735 } 1736 } 1737 isCsRetryException(Throwable e)1738 boolean isCsRetryException(Throwable e) { 1739 if ((e != null) && (e instanceof ImsException) 1740 && (((ImsException)e).getCode() 1741 == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED)) { 1742 return true; 1743 } 1744 return false; 1745 } 1746 setCsfbBundle(boolean isCsRetry)1747 private Bundle setCsfbBundle(boolean isCsRetry) { 1748 Bundle b = new Bundle(); 1749 b.putBoolean(CS_FALLBACK_SS, isCsRetry); 1750 return b; 1751 } 1752 sendResponseOrRetryOnCsfbSs(SS ss, int what, Throwable e, Object obj)1753 private void sendResponseOrRetryOnCsfbSs(SS ss, int what, Throwable e, Object obj) { 1754 if (!isCsRetryException(e)) { 1755 sendResponse(ss.mOnComplete, obj, e); 1756 return; 1757 } 1758 1759 Rlog.d(LOG_TAG, "Try CSFB: " + what); 1760 ss.mOnComplete.setData(setCsfbBundle(true)); 1761 1762 switch (what) { 1763 case EVENT_GET_CALL_FORWARD_DONE: 1764 mDefaultPhone.getCallForwardingOption(ss.mCfReason, 1765 ss.mServiceClass, 1766 ss.mOnComplete); 1767 break; 1768 case EVENT_SET_CALL_FORWARD_DONE: 1769 mDefaultPhone.setCallForwardingOption(ss.mCfAction, 1770 ss.mCfReason, 1771 ss.mDialingNumber, 1772 ss.mServiceClass, 1773 ss.mTimerSeconds, 1774 ss.mOnComplete); 1775 break; 1776 case EVENT_GET_CALL_BARRING_DONE: 1777 mDefaultPhone.getCallBarring(ss.mFacility, 1778 ss.mPassword, 1779 ss.mOnComplete, 1780 ss.mServiceClass); 1781 break; 1782 case EVENT_SET_CALL_BARRING_DONE: 1783 mDefaultPhone.setCallBarring(ss.mFacility, 1784 ss.mLockState, 1785 ss.mPassword, 1786 ss.mOnComplete, 1787 ss.mServiceClass); 1788 break; 1789 case EVENT_GET_CALL_WAITING_DONE: 1790 mDefaultPhone.getCallWaiting(ss.mOnComplete); 1791 break; 1792 case EVENT_SET_CALL_WAITING_DONE: 1793 mDefaultPhone.setCallWaiting(ss.mEnable, 1794 ss.mServiceClass, 1795 ss.mOnComplete); 1796 break; 1797 case EVENT_GET_CLIR_DONE: 1798 mDefaultPhone.getOutgoingCallerIdDisplay(ss.mOnComplete); 1799 break; 1800 case EVENT_SET_CLIR_DONE: 1801 mDefaultPhone.setOutgoingCallerIdDisplay(ss.mClirMode, ss.mOnComplete); 1802 break; 1803 case EVENT_GET_CLIP_DONE: 1804 mDefaultPhone.queryCLIP(ss.mOnComplete); 1805 break; 1806 default: 1807 break; 1808 } 1809 } 1810 1811 @Override handleMessage(Message msg)1812 public void handleMessage(Message msg) { 1813 AsyncResult ar = (AsyncResult) msg.obj; 1814 Message onComplete; 1815 SS ss = null; 1816 if (ar != null && ar.userObj instanceof SS) { 1817 ss = (SS) ar.userObj; 1818 } 1819 1820 if (DBG) logd("handleMessage what=" + msg.what); 1821 switch (msg.what) { 1822 case EVENT_SET_CALL_FORWARD_DONE: 1823 if (ar.exception == null && ss != null && 1824 (ss.mCfReason == CF_REASON_UNCONDITIONAL)) { 1825 setVoiceCallForwardingFlag(getIccRecords(), 1, isCfEnable(ss.mCfAction), 1826 ss.mDialingNumber); 1827 } 1828 if (ss != null) { 1829 sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, null); 1830 } 1831 break; 1832 1833 case EVENT_GET_CALL_FORWARD_DONE: 1834 CallForwardInfo[] cfInfos = null; 1835 if (ar.exception == null) { 1836 cfInfos = handleCfQueryResult((ImsCallForwardInfo[])ar.result); 1837 } 1838 if (ss != null) { 1839 sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, cfInfos); 1840 } 1841 break; 1842 1843 case EVENT_GET_CALL_BARRING_DONE: 1844 case EVENT_GET_CALL_WAITING_DONE: 1845 int[] ssInfos = null; 1846 if (ar.exception == null) { 1847 if (msg.what == EVENT_GET_CALL_BARRING_DONE) { 1848 ssInfos = handleCbQueryResult((ImsSsInfo[])ar.result); 1849 } else if (msg.what == EVENT_GET_CALL_WAITING_DONE) { 1850 ssInfos = handleCwQueryResult((ImsSsInfo[])ar.result); 1851 } 1852 } 1853 if (ss != null) { 1854 sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, ssInfos); 1855 } 1856 break; 1857 1858 case EVENT_GET_CLIR_DONE: 1859 ImsSsInfo ssInfo = (ImsSsInfo) ar.result; 1860 int[] clirInfo = null; 1861 if (ssInfo != null) { 1862 // Unfortunately callers still use the old {n,m} format of ImsSsInfo, so return 1863 // that for compatibility 1864 clirInfo = ssInfo.getCompatArray(ImsSsData.SS_CLIR); 1865 } 1866 if (ss != null) { 1867 sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, clirInfo); 1868 } 1869 break; 1870 1871 case EVENT_GET_CLIP_DONE: 1872 ImsSsInfo ssInfoResp = null; 1873 if (ar.exception == null && ar.result instanceof ImsSsInfo) { 1874 ssInfoResp = (ImsSsInfo) ar.result; 1875 } 1876 if (ss != null) { 1877 sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, ssInfoResp); 1878 } 1879 break; 1880 1881 case EVENT_SET_CLIR_DONE: 1882 if (ar.exception == null) { 1883 if (ss != null) { 1884 saveClirSetting(ss.mClirMode); 1885 } 1886 } 1887 // (Intentional fallthrough) 1888 case EVENT_SET_CALL_BARRING_DONE: 1889 case EVENT_SET_CALL_WAITING_DONE: 1890 if (ss != null) { 1891 sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, null); 1892 } 1893 break; 1894 1895 case EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED: 1896 if (DBG) logd("EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED"); 1897 updateDataServiceState(); 1898 break; 1899 1900 case EVENT_SERVICE_STATE_CHANGED: 1901 if (VDBG) logd("EVENT_SERVICE_STATE_CHANGED"); 1902 ar = (AsyncResult) msg.obj; 1903 ServiceState newServiceState = (ServiceState) ar.result; 1904 updateRoamingState(newServiceState); 1905 break; 1906 case EVENT_VOICE_CALL_ENDED: 1907 if (DBG) logd("Voice call ended. Handle pending updateRoamingState."); 1908 mCT.unregisterForVoiceCallEnded(this); 1909 // Get the current unmodified ServiceState from the tracker, as it has more info 1910 // about the cell roaming state. 1911 ServiceStateTracker sst = getDefaultPhone().getServiceStateTracker(); 1912 if (sst != null) { 1913 updateRoamingState(sst.mSS); 1914 } 1915 break; 1916 case EVENT_INITIATE_VOLTE_SILENT_REDIAL: { 1917 // This is a CS -> IMS redial 1918 if (VDBG) logd("EVENT_INITIATE_VOLTE_SILENT_REDIAL"); 1919 ar = (AsyncResult) msg.obj; 1920 if (ar.exception == null && ar.result != null) { 1921 SilentRedialParam result = (SilentRedialParam) ar.result; 1922 String dialString = result.dialString; 1923 int causeCode = result.causeCode; 1924 DialArgs dialArgs = result.dialArgs; 1925 if (VDBG) logd("dialString=" + dialString + " causeCode=" + causeCode); 1926 1927 try { 1928 Connection cn = dial(dialString, 1929 updateDialArgsForVolteSilentRedial(dialArgs, causeCode)); 1930 // The GSM/CDMA Connection that is owned by the GsmCdmaPhone is currently 1931 // the one with a callback registered to TelephonyConnection. Notify the 1932 // redial happened over that Phone so that it can be replaced with the 1933 // new ImsPhoneConnection. 1934 Rlog.d(LOG_TAG, "Notify volte redial connection changed cn: " + cn); 1935 if (mDefaultPhone != null) { 1936 // don't care it is null or not. 1937 mDefaultPhone.notifyRedialConnectionChanged(cn); 1938 } 1939 } catch (CallStateException e) { 1940 Rlog.e(LOG_TAG, "volte silent redial failed: " + e); 1941 if (mDefaultPhone != null) { 1942 mDefaultPhone.notifyRedialConnectionChanged(null); 1943 } 1944 } 1945 } else { 1946 if (VDBG) logd("EVENT_INITIATE_VOLTE_SILENT_REDIAL" + 1947 " has exception or empty result"); 1948 } 1949 break; 1950 } 1951 1952 default: 1953 super.handleMessage(msg); 1954 break; 1955 } 1956 } 1957 1958 /** 1959 * Listen to the IMS ECBM state change 1960 */ 1961 private ImsEcbmStateListener mImsEcbmStateListener = 1962 new ImsEcbmStateListener() { 1963 @Override 1964 public void onECBMEntered() { 1965 if (DBG) logd("onECBMEntered"); 1966 handleEnterEmergencyCallbackMode(); 1967 } 1968 1969 @Override 1970 public void onECBMExited() { 1971 if (DBG) logd("onECBMExited"); 1972 handleExitEmergencyCallbackMode(); 1973 } 1974 }; 1975 1976 @VisibleForTesting getImsEcbmStateListener()1977 public ImsEcbmStateListener getImsEcbmStateListener() { 1978 return mImsEcbmStateListener; 1979 } 1980 1981 @Override isInEmergencyCall()1982 public boolean isInEmergencyCall() { 1983 return mCT.isInEmergencyCall(); 1984 } 1985 sendEmergencyCallbackModeChange()1986 private void sendEmergencyCallbackModeChange() { 1987 // Send an Intent 1988 Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); 1989 intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, isInEcm()); 1990 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId()); 1991 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1992 if (DBG) logd("sendEmergencyCallbackModeChange: isInEcm=" + isInEcm()); 1993 } 1994 1995 @Override exitEmergencyCallbackMode()1996 public void exitEmergencyCallbackMode() { 1997 if (mWakeLock.isHeld()) { 1998 mWakeLock.release(); 1999 } 2000 if (DBG) logd("exitEmergencyCallbackMode()"); 2001 2002 // Send a message which will invoke handleExitEmergencyCallbackMode 2003 ImsEcbm ecbm; 2004 try { 2005 ecbm = mCT.getEcbmInterface(); 2006 ecbm.exitEmergencyCallbackMode(); 2007 } catch (ImsException e) { 2008 e.printStackTrace(); 2009 } 2010 } 2011 2012 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) handleEnterEmergencyCallbackMode()2013 private void handleEnterEmergencyCallbackMode() { 2014 if (DBG) logd("handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= " + isInEcm()); 2015 // if phone is not in Ecm mode, and it's changed to Ecm mode 2016 if (!isInEcm()) { 2017 setIsInEcm(true); 2018 // notify change 2019 sendEmergencyCallbackModeChange(); 2020 ((GsmCdmaPhone) mDefaultPhone).notifyEmergencyCallRegistrants(true); 2021 2022 // Post this runnable so we will automatically exit 2023 // if no one invokes exitEmergencyCallbackMode() directly. 2024 long delayInMillis = TelephonyProperties.ecm_exit_timer() 2025 .orElse(DEFAULT_ECM_EXIT_TIMER_VALUE); 2026 postDelayed(mExitEcmRunnable, delayInMillis); 2027 // We don't want to go to sleep while in Ecm 2028 mWakeLock.acquire(); 2029 } 2030 } 2031 2032 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 2033 @Override handleExitEmergencyCallbackMode()2034 protected void handleExitEmergencyCallbackMode() { 2035 if (DBG) logd("handleExitEmergencyCallbackMode: mIsPhoneInEcmState = " + isInEcm()); 2036 2037 if (isInEcm()) { 2038 setIsInEcm(false); 2039 } 2040 2041 // Remove pending exit Ecm runnable, if any 2042 removeCallbacks(mExitEcmRunnable); 2043 2044 if (mEcmExitRespRegistrant != null) { 2045 mEcmExitRespRegistrant.notifyResult(Boolean.TRUE); 2046 } 2047 2048 // release wakeLock 2049 if (mWakeLock.isHeld()) { 2050 mWakeLock.release(); 2051 } 2052 2053 // send an Intent 2054 sendEmergencyCallbackModeChange(); 2055 ((GsmCdmaPhone) mDefaultPhone).notifyEmergencyCallRegistrants(false); 2056 } 2057 2058 /** 2059 * Handle to cancel or restart Ecm timer in emergency call back mode if action is 2060 * CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled; otherwise, restart 2061 * Ecm timer and notify apps the timer is restarted. 2062 */ handleTimerInEmergencyCallbackMode(int action)2063 void handleTimerInEmergencyCallbackMode(int action) { 2064 switch (action) { 2065 case CANCEL_ECM_TIMER: 2066 removeCallbacks(mExitEcmRunnable); 2067 ((GsmCdmaPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.TRUE); 2068 setEcmCanceledForEmergency(true /*isCanceled*/); 2069 break; 2070 case RESTART_ECM_TIMER: 2071 long delayInMillis = TelephonyProperties.ecm_exit_timer() 2072 .orElse(DEFAULT_ECM_EXIT_TIMER_VALUE); 2073 postDelayed(mExitEcmRunnable, delayInMillis); 2074 ((GsmCdmaPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.FALSE); 2075 setEcmCanceledForEmergency(false /*isCanceled*/); 2076 break; 2077 default: 2078 loge("handleTimerInEmergencyCallbackMode, unsupported action " + action); 2079 } 2080 } 2081 2082 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 2083 @Override setOnEcbModeExitResponse(Handler h, int what, Object obj)2084 public void setOnEcbModeExitResponse(Handler h, int what, Object obj) { 2085 mEcmExitRespRegistrant = new Registrant(h, what, obj); 2086 } 2087 2088 @Override unsetOnEcbModeExitResponse(Handler h)2089 public void unsetOnEcbModeExitResponse(Handler h) { 2090 mEcmExitRespRegistrant.clear(); 2091 } 2092 onFeatureCapabilityChanged()2093 public void onFeatureCapabilityChanged() { 2094 mDefaultPhone.getServiceStateTracker().onImsCapabilityChanged(); 2095 } 2096 2097 @Override isImsCapabilityAvailable(int capability, int regTech)2098 public boolean isImsCapabilityAvailable(int capability, int regTech) throws ImsException { 2099 return mCT.isImsCapabilityAvailable(capability, regTech); 2100 } 2101 2102 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 2103 @Override isVolteEnabled()2104 public boolean isVolteEnabled() { 2105 return isVoiceOverCellularImsEnabled(); 2106 } 2107 2108 @Override isVoiceOverCellularImsEnabled()2109 public boolean isVoiceOverCellularImsEnabled() { 2110 return mCT.isVoiceOverCellularImsEnabled(); 2111 } 2112 2113 @Override isWifiCallingEnabled()2114 public boolean isWifiCallingEnabled() { 2115 return mCT.isVowifiEnabled(); 2116 } 2117 2118 @Override isVideoEnabled()2119 public boolean isVideoEnabled() { 2120 return mCT.isVideoCallEnabled(); 2121 } 2122 2123 @Override getImsRegistrationTech()2124 public int getImsRegistrationTech() { 2125 return mCT.getImsRegistrationTech(); 2126 } 2127 2128 @Override getImsRegistrationTech(Consumer<Integer> callback)2129 public void getImsRegistrationTech(Consumer<Integer> callback) { 2130 mCT.getImsRegistrationTech(callback); 2131 } 2132 2133 @Override getImsRegistrationState(Consumer<Integer> callback)2134 public void getImsRegistrationState(Consumer<Integer> callback) { 2135 callback.accept(mImsMmTelRegistrationHelper.getImsRegistrationState()); 2136 } 2137 2138 @Override getDefaultPhone()2139 public Phone getDefaultPhone() { 2140 return mDefaultPhone; 2141 } 2142 2143 @Override isImsRegistered()2144 public boolean isImsRegistered() { 2145 return mImsMmTelRegistrationHelper.isImsRegistered(); 2146 } 2147 2148 // Not used, but not removed due to UnsupportedAppUsage tag. 2149 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) setImsRegistered(boolean isRegistered)2150 public void setImsRegistered(boolean isRegistered) { 2151 mImsMmTelRegistrationHelper.updateRegistrationState( 2152 isRegistered ? RegistrationManager.REGISTRATION_STATE_REGISTERED : 2153 RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); 2154 } 2155 2156 @Override callEndCleanupHandOverCallIfAny()2157 public void callEndCleanupHandOverCallIfAny() { 2158 mCT.callEndCleanupHandOverCallIfAny(); 2159 } 2160 2161 private BroadcastReceiver mResultReceiver = new BroadcastReceiver() { 2162 @Override 2163 public void onReceive(Context context, Intent intent) { 2164 // Add notification only if alert was not shown by WfcSettings 2165 if (getResultCode() == Activity.RESULT_OK) { 2166 // Default result code (as passed to sendOrderedBroadcast) 2167 // means that intent was not received by WfcSettings. 2168 2169 CharSequence title = 2170 intent.getCharSequenceExtra(EXTRA_WFC_REGISTRATION_FAILURE_TITLE); 2171 CharSequence messageAlert = 2172 intent.getCharSequenceExtra(EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE); 2173 CharSequence messageNotification = 2174 intent.getCharSequenceExtra(EXTRA_KEY_NOTIFICATION_MESSAGE); 2175 2176 Intent resultIntent = new Intent(Intent.ACTION_MAIN); 2177 // Note: If the classname below is ever removed, the call to 2178 // PendingIntent.getActivity should also specify FLAG_IMMUTABLE to ensure the 2179 // pending intent cannot be tampered with. 2180 resultIntent.setClassName("com.android.settings", 2181 "com.android.settings.Settings$WifiCallingSettingsActivity"); 2182 resultIntent.putExtra(EXTRA_KEY_ALERT_SHOW, true); 2183 resultIntent.putExtra(EXTRA_WFC_REGISTRATION_FAILURE_TITLE, title); 2184 resultIntent.putExtra(EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE, messageAlert); 2185 PendingIntent resultPendingIntent = 2186 PendingIntent.getActivity( 2187 mContext, 2188 0, 2189 resultIntent, 2190 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE 2191 ); 2192 2193 final Notification notification = new Notification.Builder(mContext) 2194 .setSmallIcon(android.R.drawable.stat_sys_warning) 2195 .setContentTitle(title) 2196 .setContentText(messageNotification) 2197 .setAutoCancel(true) 2198 .setContentIntent(resultPendingIntent) 2199 .setStyle(new Notification.BigTextStyle() 2200 .bigText(messageNotification)) 2201 .setChannelId(NotificationChannelController.CHANNEL_ID_WFC) 2202 .build(); 2203 final String notificationTag = "wifi_calling"; 2204 final int notificationId = 1; 2205 2206 NotificationManager notificationManager = 2207 (NotificationManager) mContext.getSystemService( 2208 Context.NOTIFICATION_SERVICE); 2209 notificationManager.notify(notificationTag, notificationId, 2210 notification); 2211 } 2212 } 2213 }; 2214 2215 /** 2216 * Show notification in case of some error codes. 2217 */ processDisconnectReason(ImsReasonInfo imsReasonInfo)2218 public void processDisconnectReason(ImsReasonInfo imsReasonInfo) { 2219 if (imsReasonInfo.mCode == imsReasonInfo.CODE_REGISTRATION_ERROR 2220 && imsReasonInfo.mExtraMessage != null) { 2221 // Suppress WFC Registration notifications if WFC is not enabled by the user. 2222 if (mImsManagerFactory.create(mContext, mPhoneId).isWfcEnabledByUser()) { 2223 processWfcDisconnectForNotification(imsReasonInfo); 2224 } 2225 } 2226 } 2227 2228 // Processes an IMS disconnect cause for possible WFC registration errors and optionally 2229 // disable WFC. processWfcDisconnectForNotification(ImsReasonInfo imsReasonInfo)2230 private void processWfcDisconnectForNotification(ImsReasonInfo imsReasonInfo) { 2231 CarrierConfigManager configManager = 2232 (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); 2233 if (configManager == null) { 2234 loge("processDisconnectReason: CarrierConfigManager is not ready"); 2235 return; 2236 } 2237 PersistableBundle pb = configManager.getConfigForSubId(getSubId()); 2238 if (pb == null) { 2239 loge("processDisconnectReason: no config for subId " + getSubId()); 2240 return; 2241 } 2242 final String[] wfcOperatorErrorCodes = 2243 pb.getStringArray( 2244 CarrierConfigManager.KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY); 2245 if (wfcOperatorErrorCodes == null) { 2246 // no operator-specific error codes 2247 return; 2248 } 2249 2250 final String[] wfcOperatorErrorAlertMessages = 2251 mContext.getResources().getStringArray( 2252 com.android.internal.R.array.wfcOperatorErrorAlertMessages); 2253 final String[] wfcOperatorErrorNotificationMessages = 2254 mContext.getResources().getStringArray( 2255 com.android.internal.R.array.wfcOperatorErrorNotificationMessages); 2256 2257 for (int i = 0; i < wfcOperatorErrorCodes.length; i++) { 2258 String[] codes = wfcOperatorErrorCodes[i].split("\\|"); 2259 if (codes.length != 2) { 2260 loge("Invalid carrier config: " + wfcOperatorErrorCodes[i]); 2261 continue; 2262 } 2263 2264 // Match error code. 2265 if (!imsReasonInfo.mExtraMessage.startsWith( 2266 codes[0])) { 2267 continue; 2268 } 2269 // If there is no delimiter at the end of error code string 2270 // then we need to verify that we are not matching partial code. 2271 // EXAMPLE: "REG9" must not match "REG99". 2272 // NOTE: Error code must not be empty. 2273 int codeStringLength = codes[0].length(); 2274 char lastChar = codes[0].charAt(codeStringLength - 1); 2275 if (Character.isLetterOrDigit(lastChar)) { 2276 if (imsReasonInfo.mExtraMessage.length() > codeStringLength) { 2277 char nextChar = imsReasonInfo.mExtraMessage.charAt(codeStringLength); 2278 if (Character.isLetterOrDigit(nextChar)) { 2279 continue; 2280 } 2281 } 2282 } 2283 2284 final CharSequence title = mContext.getText( 2285 com.android.internal.R.string.wfcRegErrorTitle); 2286 2287 int idx = Integer.parseInt(codes[1]); 2288 if (idx < 0 2289 || idx >= wfcOperatorErrorAlertMessages.length 2290 || idx >= wfcOperatorErrorNotificationMessages.length) { 2291 loge("Invalid index: " + wfcOperatorErrorCodes[i]); 2292 continue; 2293 } 2294 String messageAlert = imsReasonInfo.mExtraMessage; 2295 String messageNotification = imsReasonInfo.mExtraMessage; 2296 if (!wfcOperatorErrorAlertMessages[idx].isEmpty()) { 2297 messageAlert = String.format( 2298 wfcOperatorErrorAlertMessages[idx], 2299 imsReasonInfo.mExtraMessage); // Fill IMS error code into alert message 2300 } 2301 if (!wfcOperatorErrorNotificationMessages[idx].isEmpty()) { 2302 messageNotification = String.format( 2303 wfcOperatorErrorNotificationMessages[idx], 2304 imsReasonInfo.mExtraMessage); // Fill IMS error code into notification 2305 } 2306 2307 // If WfcSettings are active then alert will be shown 2308 // otherwise notification will be added. 2309 Intent intent = new Intent( 2310 android.telephony.ims.ImsManager.ACTION_WFC_IMS_REGISTRATION_ERROR); 2311 intent.putExtra(EXTRA_WFC_REGISTRATION_FAILURE_TITLE, title); 2312 intent.putExtra(EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE, messageAlert); 2313 intent.putExtra(EXTRA_KEY_NOTIFICATION_MESSAGE, messageNotification); 2314 mContext.sendOrderedBroadcast(intent, null, mResultReceiver, 2315 null, Activity.RESULT_OK, null, null); 2316 2317 // We can only match a single error code 2318 // so should break the loop after a successful match. 2319 break; 2320 } 2321 } 2322 2323 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 2324 @Override isUtEnabled()2325 public boolean isUtEnabled() { 2326 return mCT.isUtEnabled(); 2327 } 2328 2329 @Override sendEmergencyCallStateChange(boolean callActive)2330 public void sendEmergencyCallStateChange(boolean callActive) { 2331 mDefaultPhone.sendEmergencyCallStateChange(callActive); 2332 } 2333 2334 @Override setBroadcastEmergencyCallStateChanges(boolean broadcast)2335 public void setBroadcastEmergencyCallStateChanges(boolean broadcast) { 2336 mDefaultPhone.setBroadcastEmergencyCallStateChanges(broadcast); 2337 } 2338 2339 @VisibleForTesting getWakeLock()2340 public PowerManager.WakeLock getWakeLock() { 2341 return mWakeLock; 2342 } 2343 2344 /** 2345 * Update roaming state and WFC mode in the following situations: 2346 * 1) voice is in service. 2347 * 2) data is in service and it is not IWLAN (if in legacy mode). 2348 * @param ss non-null ServiceState 2349 */ updateRoamingState(ServiceState ss)2350 private void updateRoamingState(ServiceState ss) { 2351 if (ss == null) { 2352 loge("updateRoamingState: null ServiceState!"); 2353 return; 2354 } 2355 boolean newRoamingState = ss.getRoaming(); 2356 // Do not recalculate if there is no change to state. 2357 if (mLastKnownRoamingState == newRoamingState) { 2358 return; 2359 } 2360 boolean isInService = (ss.getState() == ServiceState.STATE_IN_SERVICE 2361 || ss.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE); 2362 // If we are not IN_SERVICE for voice or data, ignore change roaming state, as we always 2363 // move to home in this case. 2364 if (!isInService || !mDefaultPhone.isRadioOn()) { 2365 logi("updateRoamingState: we are not IN_SERVICE, ignoring roaming change."); 2366 return; 2367 } 2368 // We ignore roaming changes when moving to IWLAN because it always sets the roaming 2369 // mode to home and masks the actual cellular roaming status if voice is not registered. If 2370 // we just moved to IWLAN because WFC roaming mode is IWLAN preferred and WFC home mode is 2371 // cell preferred, we can get into a condition where the modem keeps bouncing between 2372 // IWLAN->cell->IWLAN->cell... 2373 if (isCsNotInServiceAndPsWwanReportingWlan(ss)) { 2374 logi("updateRoamingState: IWLAN masking roaming, ignore roaming change."); 2375 return; 2376 } 2377 if (mCT.getState() == PhoneConstants.State.IDLE) { 2378 if (DBG) logd("updateRoamingState now: " + newRoamingState); 2379 mLastKnownRoamingState = newRoamingState; 2380 CarrierConfigManager configManager = (CarrierConfigManager) 2381 getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 2382 // Don't set wfc mode if carrierconfig has not loaded. It will be set by GsmCdmaPhone 2383 // when receives ACTION_CARRIER_CONFIG_CHANGED broadcast. 2384 if (configManager != null && CarrierConfigManager.isConfigForIdentifiedCarrier( 2385 configManager.getConfigForSubId(getSubId()))) { 2386 ImsManager imsManager = mImsManagerFactory.create(mContext, mPhoneId); 2387 imsManager.setWfcMode(imsManager.getWfcMode(newRoamingState), newRoamingState); 2388 } 2389 } else { 2390 if (DBG) logd("updateRoamingState postponed: " + newRoamingState); 2391 mCT.registerForVoiceCallEnded(this, EVENT_VOICE_CALL_ENDED, null); 2392 } 2393 } 2394 2395 /** 2396 * In legacy mode, data registration will report IWLAN when we are using WLAN for data, 2397 * effectively masking the true roaming state of the device if voice is not registered. 2398 * 2399 * @return true if we are reporting not in service for CS domain over WWAN transport and WLAN 2400 * for PS domain over WWAN transport. 2401 */ isCsNotInServiceAndPsWwanReportingWlan(ServiceState ss)2402 private boolean isCsNotInServiceAndPsWwanReportingWlan(ServiceState ss) { 2403 TransportManager tm = mDefaultPhone.getTransportManager(); 2404 // We can not get into this condition if we are in AP-Assisted mode. 2405 if (tm == null || !tm.isInLegacyMode()) { 2406 return false; 2407 } 2408 NetworkRegistrationInfo csInfo = ss.getNetworkRegistrationInfo( 2409 NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 2410 NetworkRegistrationInfo psInfo = ss.getNetworkRegistrationInfo( 2411 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 2412 // We will return roaming state correctly if the CS domain is in service because 2413 // ss.getRoaming() returns isVoiceRoaming||isDataRoaming result and isDataRoaming==false 2414 // when the modem reports IWLAN RAT. 2415 return psInfo != null && csInfo != null && !csInfo.isInService() 2416 && psInfo.getAccessNetworkTechnology() == TelephonyManager.NETWORK_TYPE_IWLAN; 2417 } 2418 getImsMmTelRegistrationCallback()2419 public RegistrationManager.RegistrationCallback getImsMmTelRegistrationCallback() { 2420 return mImsMmTelRegistrationHelper.getCallback(); 2421 } 2422 2423 /** 2424 * Reset the IMS registration state. 2425 */ resetImsRegistrationState()2426 public void resetImsRegistrationState() { 2427 if (DBG) logd("resetImsRegistrationState"); 2428 mImsMmTelRegistrationHelper.reset(); 2429 } 2430 2431 private ImsRegistrationCallbackHelper.ImsRegistrationUpdate mMmTelRegistrationUpdate = new 2432 ImsRegistrationCallbackHelper.ImsRegistrationUpdate() { 2433 @Override 2434 public void handleImsRegistered(int imsRadioTech) { 2435 if (DBG) { 2436 logd("handleImsRegistered: onImsMmTelConnected imsRadioTech=" 2437 + AccessNetworkConstants.transportTypeToString(imsRadioTech)); 2438 } 2439 mRegLocalLog.log("handleImsRegistered: onImsMmTelConnected imsRadioTech=" 2440 + AccessNetworkConstants.transportTypeToString(imsRadioTech)); 2441 setServiceState(ServiceState.STATE_IN_SERVICE); 2442 getDefaultPhone().setImsRegistrationState(true); 2443 mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.CONNECTED, null); 2444 mImsStats.onImsRegistered(imsRadioTech); 2445 } 2446 2447 @Override 2448 public void handleImsRegistering(int imsRadioTech) { 2449 if (DBG) { 2450 logd("handleImsRegistering: onImsMmTelProgressing imsRadioTech=" 2451 + AccessNetworkConstants.transportTypeToString(imsRadioTech)); 2452 } 2453 mRegLocalLog.log("handleImsRegistering: onImsMmTelProgressing imsRadioTech=" 2454 + AccessNetworkConstants.transportTypeToString(imsRadioTech)); 2455 setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 2456 getDefaultPhone().setImsRegistrationState(false); 2457 mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.PROGRESSING, 2458 null); 2459 mImsStats.onImsRegistering(imsRadioTech); 2460 } 2461 2462 @Override 2463 public void handleImsUnregistered(ImsReasonInfo imsReasonInfo) { 2464 if (DBG) { 2465 logd("handleImsUnregistered: onImsMmTelDisconnected imsReasonInfo=" 2466 + imsReasonInfo); 2467 } 2468 mRegLocalLog.log("handleImsUnregistered: onImsMmTelDisconnected imsRadioTech=" 2469 + imsReasonInfo); 2470 setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 2471 processDisconnectReason(imsReasonInfo); 2472 getDefaultPhone().setImsRegistrationState(false); 2473 mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.DISCONNECTED, 2474 imsReasonInfo); 2475 mImsStats.onImsUnregistered(imsReasonInfo); 2476 } 2477 2478 @Override 2479 public void handleImsSubscriberAssociatedUriChanged(Uri[] uris) { 2480 if (DBG) logd("handleImsSubscriberAssociatedUriChanged"); 2481 setCurrentSubscriberUris(uris); 2482 } 2483 }; 2484 getIccRecords()2485 public IccRecords getIccRecords() { 2486 return mDefaultPhone.getIccRecords(); 2487 } 2488 updateDialArgsForVolteSilentRedial(DialArgs dialArgs, int causeCode)2489 public DialArgs updateDialArgsForVolteSilentRedial(DialArgs dialArgs, int causeCode) { 2490 if (dialArgs != null) { 2491 ImsPhone.ImsDialArgs.Builder imsDialArgsBuilder; 2492 imsDialArgsBuilder = ImsPhone.ImsDialArgs.Builder.from(dialArgs); 2493 2494 Bundle extras = new Bundle(dialArgs.intentExtras); 2495 if (causeCode == CallFailCause.EMC_REDIAL_ON_VOWIFI && isWifiCallingEnabled()) { 2496 extras.putString(ImsCallProfile.EXTRA_CALL_RAT_TYPE, 2497 String.valueOf(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)); 2498 logd("trigger VoWifi emergency call"); 2499 imsDialArgsBuilder.setIntentExtras(extras); 2500 } else if (causeCode == CallFailCause.EMC_REDIAL_ON_IMS) { 2501 logd("trigger VoLte emergency call"); 2502 } 2503 return imsDialArgsBuilder.build(); 2504 } 2505 return new DialArgs.Builder<>().build(); 2506 } 2507 2508 @Override getVoiceCallSessionStats()2509 public VoiceCallSessionStats getVoiceCallSessionStats() { 2510 return mDefaultPhone.getVoiceCallSessionStats(); 2511 } 2512 2513 /** Returns the {@link ImsStats} for this IMS phone. */ getImsStats()2514 public ImsStats getImsStats() { 2515 return mImsStats; 2516 } 2517 2518 /** Sets the {@link ImsStats} mock for this IMS phone during unit testing. */ 2519 @VisibleForTesting setImsStats(ImsStats imsStats)2520 public void setImsStats(ImsStats imsStats) { 2521 mImsStats = imsStats; 2522 } 2523 hasAliveCall()2524 public boolean hasAliveCall() { 2525 return (getForegroundCall().getState() != Call.State.IDLE || 2526 getBackgroundCall().getState() != Call.State.IDLE); 2527 } 2528 getLastKnownRoamingState()2529 public boolean getLastKnownRoamingState() { 2530 return mLastKnownRoamingState; 2531 } 2532 2533 @Override dump(FileDescriptor fd, PrintWriter printWriter, String[] args)2534 public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { 2535 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 2536 pw.println("ImsPhone extends:"); 2537 super.dump(fd, pw, args); 2538 pw.flush(); 2539 2540 pw.println("ImsPhone:"); 2541 pw.println(" mDefaultPhone = " + mDefaultPhone); 2542 pw.println(" mPendingMMIs = " + mPendingMMIs); 2543 pw.println(" mPostDialHandler = " + mPostDialHandler); 2544 pw.println(" mSS = " + mSS); 2545 pw.println(" mWakeLock = " + mWakeLock); 2546 pw.println(" mIsPhoneInEcmState = " + isInEcm()); 2547 pw.println(" mEcmExitRespRegistrant = " + mEcmExitRespRegistrant); 2548 pw.println(" mSilentRedialRegistrants = " + mSilentRedialRegistrants); 2549 pw.println(" mImsMmTelRegistrationState = " 2550 + mImsMmTelRegistrationHelper.getImsRegistrationState()); 2551 pw.println(" mLastKnownRoamingState = " + mLastKnownRoamingState); 2552 pw.println(" mSsnRegistrants = " + mSsnRegistrants); 2553 pw.println(" Registration Log:"); 2554 pw.increaseIndent(); 2555 mRegLocalLog.dump(pw); 2556 pw.decreaseIndent(); 2557 pw.flush(); 2558 } 2559 logi(String s)2560 private void logi(String s) { 2561 Rlog.i(LOG_TAG, "[" + mPhoneId + "] " + s); 2562 } 2563 logv(String s)2564 private void logv(String s) { 2565 Rlog.v(LOG_TAG, "[" + mPhoneId + "] " + s); 2566 } 2567 logd(String s)2568 private void logd(String s) { 2569 Rlog.d(LOG_TAG, "[" + mPhoneId + "] " + s); 2570 } 2571 loge(String s)2572 private void loge(String s) { 2573 Rlog.e(LOG_TAG, "[" + mPhoneId + "] " + s); 2574 } 2575 } 2576