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