| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.telephony; |
| |
| import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; |
| |
| import static com.android.internal.telephony.CommandException.Error.GENERIC_FAILURE; |
| import static com.android.internal.telephony.CommandException.Error.SIM_BUSY; |
| import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE; |
| import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE; |
| import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE; |
| import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION; |
| import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL; |
| import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL; |
| import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY; |
| import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE; |
| import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY; |
| import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL; |
| import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.ActivityManager; |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.content.BroadcastReceiver; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.SharedPreferences; |
| import android.content.pm.PackageManager; |
| import android.database.SQLException; |
| import android.hardware.radio.modem.ImeiInfo; |
| import android.net.Uri; |
| import android.os.AsyncResult; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.HandlerExecutor; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.PersistableBundle; |
| import android.os.PowerManager; |
| import android.os.Registrant; |
| import android.os.RegistrantList; |
| import android.os.ResultReceiver; |
| import android.os.SystemProperties; |
| import android.os.UserHandle; |
| import android.os.WorkSource; |
| import android.preference.PreferenceManager; |
| import android.provider.DeviceConfig; |
| import android.provider.Settings; |
| import android.provider.Telephony; |
| import android.sysprop.TelephonyProperties; |
| import android.telecom.PhoneAccount; |
| import android.telecom.PhoneAccountHandle; |
| import android.telecom.TelecomManager; |
| import android.telecom.VideoProfile; |
| import android.telephony.AccessNetworkConstants.TransportType; |
| import android.telephony.Annotation.DataActivityType; |
| import android.telephony.Annotation.RadioPowerState; |
| import android.telephony.BarringInfo; |
| import android.telephony.CarrierConfigManager; |
| import android.telephony.CellBroadcastIdRange; |
| import android.telephony.CellIdentity; |
| import android.telephony.CellularIdentifierDisclosure; |
| import android.telephony.ImsiEncryptionInfo; |
| import android.telephony.LinkCapacityEstimate; |
| import android.telephony.NetworkScanRequest; |
| import android.telephony.PhoneNumberUtils; |
| import android.telephony.RadioAccessFamily; |
| import android.telephony.SecurityAlgorithmUpdate; |
| import android.telephony.ServiceState; |
| import android.telephony.ServiceState.RilRadioTechnology; |
| import android.telephony.SubscriptionInfo; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyManager; |
| import android.telephony.UiccAccessRule; |
| import android.telephony.UssdResponse; |
| import android.telephony.ims.ImsCallProfile; |
| import android.text.TextUtils; |
| import android.util.ArraySet; |
| import android.util.Log; |
| import android.util.Pair; |
| |
| import com.android.ims.ImsManager; |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.telephony.cdma.CdmaMmiCode; |
| import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager; |
| import com.android.internal.telephony.data.AccessNetworksManager; |
| import com.android.internal.telephony.data.DataNetworkController; |
| import com.android.internal.telephony.data.LinkBandwidthEstimator; |
| import com.android.internal.telephony.domainselection.DomainSelectionResolver; |
| import com.android.internal.telephony.emergency.EmergencyNumberTracker; |
| import com.android.internal.telephony.emergency.EmergencyStateTracker; |
| import com.android.internal.telephony.flags.FeatureFlags; |
| import com.android.internal.telephony.gsm.GsmMmiCode; |
| import com.android.internal.telephony.gsm.SsData; |
| import com.android.internal.telephony.gsm.SuppServiceNotification; |
| import com.android.internal.telephony.imsphone.ImsPhone; |
| import com.android.internal.telephony.imsphone.ImsPhoneCallTracker; |
| import com.android.internal.telephony.imsphone.ImsPhoneMmiCode; |
| import com.android.internal.telephony.metrics.TelephonyMetrics; |
| import com.android.internal.telephony.metrics.VoiceCallSessionStats; |
| import com.android.internal.telephony.security.CellularIdentifierDisclosureNotifier; |
| import com.android.internal.telephony.security.CellularNetworkSecuritySafetySource; |
| import com.android.internal.telephony.security.NullCipherNotifier; |
| import com.android.internal.telephony.subscription.SubscriptionInfoInternal; |
| import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback; |
| import com.android.internal.telephony.test.SimulatedRadioControl; |
| import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType; |
| import com.android.internal.telephony.uicc.IccCardStatus; |
| import com.android.internal.telephony.uicc.IccException; |
| import com.android.internal.telephony.uicc.IccRecords; |
| import com.android.internal.telephony.uicc.IccUtils; |
| import com.android.internal.telephony.uicc.IccVmNotSupportedException; |
| import com.android.internal.telephony.uicc.IsimRecords; |
| import com.android.internal.telephony.uicc.IsimUiccRecords; |
| import com.android.internal.telephony.uicc.RuimRecords; |
| import com.android.internal.telephony.uicc.SIMRecords; |
| import com.android.internal.telephony.uicc.UiccCardApplication; |
| import com.android.internal.telephony.uicc.UiccController; |
| import com.android.internal.telephony.uicc.UiccPort; |
| import com.android.internal.telephony.uicc.UiccProfile; |
| import com.android.internal.telephony.uicc.UiccSlot; |
| import com.android.internal.telephony.util.ArrayUtils; |
| import com.android.telephony.Rlog; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Set; |
| import java.util.function.Consumer; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * {@hide} |
| */ |
| public class GsmCdmaPhone extends Phone { |
| // NOTE that LOG_TAG here is "GsmCdma", which means that log messages |
| // from this file will go into the radio log rather than the main |
| // log. (Use "adb logcat -b radio" to see them.) |
| public static final String LOG_TAG = "GsmCdmaPhone"; |
| private static final boolean DBG = true; |
| private static final boolean VDBG = false; /* STOPSHIP if true */ |
| |
| /** Required throughput change between unsolicited LinkCapacityEstimate reports. */ |
| private static final int REPORTING_HYSTERESIS_KBPS = 50; |
| /** Minimum time between unsolicited LinkCapacityEstimate reports. */ |
| private static final int REPORTING_HYSTERESIS_MILLIS = 3000; |
| |
| //GSM |
| // Key used to read/write voice mail number |
| private static final String VM_NUMBER = "vm_number_key"; |
| // Key used to read/write the SIM IMSI used for storing the voice mail |
| private static final String VM_SIM_IMSI = "vm_sim_imsi_key"; |
| /** List of Registrants to receive Supplementary Service Notifications. */ |
| // Key used to read/write the current sub Id. Updated on SIM loaded. |
| public static final String CURR_SUBID = "curr_subid"; |
| private RegistrantList mSsnRegistrants = new RegistrantList(); |
| |
| //CDMA |
| // Default Emergency Callback Mode exit timer |
| private static final long DEFAULT_ECM_EXIT_TIMER_VALUE = 300000; |
| private static final String VM_NUMBER_CDMA = "vm_number_key_cdma"; |
| public static final int RESTART_ECM_TIMER = 0; // restart Ecm timer |
| public static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer |
| private CdmaSubscriptionSourceManager mCdmaSSM; |
| public int mCdmaSubscriptionSource = CdmaSubscriptionSourceManager.SUBSCRIPTION_SOURCE_UNKNOWN; |
| private PowerManager.WakeLock mWakeLock; |
| // mEcmExitRespRegistrant is informed after the phone has been exited |
| @UnsupportedAppUsage |
| private Registrant mEcmExitRespRegistrant; |
| private String mEsn; |
| private String mMeid; |
| // string to define how the carrier specifies its own ota sp number |
| private String mCarrierOtaSpNumSchema; |
| private Boolean mUiccApplicationsEnabled = null; |
| // keeps track of when we have triggered an emergency call due to the ril.test.emergencynumber |
| // param being set and we should generate a simulated exit from the modem upon exit of ECbM. |
| private boolean mIsTestingEmergencyCallbackMode = false; |
| @VisibleForTesting |
| public static int ENABLE_UICC_APPS_MAX_RETRIES = 3; |
| private static final int REAPPLY_UICC_APPS_SETTING_RETRY_TIME_GAP_IN_MS = 5000; |
| |
| // A runnable which is used to automatically exit from Ecm after a period of time. |
| private Runnable mExitEcmRunnable = new Runnable() { |
| @Override |
| public void run() { |
| exitEmergencyCallbackMode(); |
| } |
| }; |
| public static final String PROPERTY_CDMA_HOME_OPERATOR_NUMERIC = |
| "ro.cdma.home.operator.numeric"; |
| |
| //CDMALTE |
| /** PHONE_TYPE_CDMA_LTE in addition to RuimRecords needs access to SIMRecords and |
| * IsimUiccRecords |
| */ |
| private SIMRecords mSimRecords; |
| |
| // For non-persisted manual network selection |
| private String mManualNetworkSelectionPlmn; |
| |
| //Common |
| // Instance Variables |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private IsimUiccRecords mIsimUiccRecords; |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public GsmCdmaCallTracker mCT; |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public ServiceStateTracker mSST; |
| public EmergencyNumberTracker mEmergencyNumberTracker; |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private ArrayList <MmiCode> mPendingMMIs = new ArrayList<MmiCode>(); |
| private IccPhoneBookInterfaceManager mIccPhoneBookIntManager; |
| |
| private int mPrecisePhoneType; |
| |
| // mEcmTimerResetRegistrants are informed after Ecm timer is canceled or re-started |
| private final RegistrantList mEcmTimerResetRegistrants = new RegistrantList(); |
| |
| private final RegistrantList mVolteSilentRedialRegistrants = new RegistrantList(); |
| private DialArgs mDialArgs = null; |
| private final RegistrantList mEmergencyDomainSelectedRegistrants = new RegistrantList(); |
| private String mImei; |
| private String mImeiSv; |
| private String mVmNumber; |
| private int mImeiType = IMEI_TYPE_UNKNOWN; |
| private int mSimState = TelephonyManager.SIM_STATE_UNKNOWN; |
| |
| @VisibleForTesting |
| public CellBroadcastConfigTracker mCellBroadcastConfigTracker = |
| CellBroadcastConfigTracker.make(this, null, true); |
| |
| private boolean mIsNullCipherAndIntegritySupported = false; |
| private boolean mIsIdentifierDisclosureTransparencySupported = false; |
| private boolean mIsNullCipherNotificationSupported = false; |
| |
| // Create Cfu (Call forward unconditional) so that dialing number & |
| // mOnComplete (Message object passed by client) can be packed & |
| // given as a single Cfu object as user data to RIL. |
| private static class Cfu { |
| final String mSetCfNumber; |
| final Message mOnComplete; |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| Cfu(String cfNumber, Message onComplete) { |
| mSetCfNumber = cfNumber; |
| mOnComplete = onComplete; |
| } |
| } |
| |
| /** |
| * Used to create ImsManager instances, which may be injected during testing. |
| */ |
| @VisibleForTesting |
| public interface ImsManagerFactory { |
| /** |
| * Create a new instance of ImsManager for the specified phoneId. |
| */ |
| ImsManager create(Context context, int phoneId); |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private IccSmsInterfaceManager mIccSmsInterfaceManager; |
| |
| private boolean mResetModemOnRadioTechnologyChange = false; |
| private boolean mSsOverCdmaSupported = false; |
| |
| private int mRilVersion; |
| private boolean mBroadcastEmergencyCallStateChanges = false; |
| private @ServiceState.RegState int mTelecomVoiceServiceStateOverride = |
| ServiceState.STATE_OUT_OF_SERVICE; |
| |
| private CarrierKeyDownloadManager mCDM; |
| private CarrierInfoManager mCIM; |
| |
| private final ImsManagerFactory mImsManagerFactory; |
| private final CarrierPrivilegesTracker mCarrierPrivilegesTracker; |
| |
| private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener; |
| private final CallWaitingController mCallWaitingController; |
| |
| private CellularNetworkSecuritySafetySource mSafetySource; |
| private CellularIdentifierDisclosureNotifier mIdentifierDisclosureNotifier; |
| private NullCipherNotifier mNullCipherNotifier; |
| |
| /** |
| * Temporary placeholder variables until b/312788638 is resolved, whereupon these should be |
| * ported to TelephonyManager. |
| */ |
| // Set via Carrier Config |
| private static final Integer N1_MODE_DISALLOWED_REASON_CARRIER = 1; |
| // Set via a call to the method on Phone; the only caller is IMS, and all of this code will |
| // need to be updated to a voting mechanism (...enabled for reason...) if additional callers |
| // are desired. |
| private static final Integer N1_MODE_DISALLOWED_REASON_IMS = 2; |
| |
| // Set of use callers/reasons why N1 Mode is disallowed. If the set is empty, it's allowed. |
| private final Set<Integer> mN1ModeDisallowedReasons = new ArraySet<>(); |
| |
| // If this value is null, then the modem value is unknown. If a caller explicitly sets the |
| // N1 mode, this value will be initialized before any attempt to set the value in the modem. |
| private Boolean mModemN1Mode = null; |
| |
| // Constructors |
| |
| public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId, |
| int precisePhoneType, TelephonyComponentFactory telephonyComponentFactory, |
| @NonNull FeatureFlags featureFlags) { |
| this(context, ci, notifier, false, phoneId, precisePhoneType, telephonyComponentFactory, |
| featureFlags); |
| } |
| |
| public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, |
| boolean unitTestMode, int phoneId, int precisePhoneType, |
| TelephonyComponentFactory telephonyComponentFactory, |
| @NonNull FeatureFlags featureFlags) { |
| this(context, ci, notifier, |
| unitTestMode, phoneId, precisePhoneType, |
| telephonyComponentFactory, |
| ImsManager::getInstance, featureFlags); |
| } |
| |
| public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, |
| boolean unitTestMode, int phoneId, int precisePhoneType, |
| TelephonyComponentFactory telephonyComponentFactory, |
| ImsManagerFactory imsManagerFactory, @NonNull FeatureFlags featureFlags) { |
| super(precisePhoneType == PhoneConstants.PHONE_TYPE_GSM ? "GSM" : "CDMA", |
| notifier, context, ci, unitTestMode, phoneId, telephonyComponentFactory, |
| featureFlags); |
| |
| // phone type needs to be set before other initialization as other objects rely on it |
| mPrecisePhoneType = precisePhoneType; |
| mVoiceCallSessionStats = new VoiceCallSessionStats(mPhoneId, this, featureFlags); |
| mImsManagerFactory = imsManagerFactory; |
| initOnce(ci); |
| initRatSpecific(precisePhoneType); |
| // CarrierSignalAgent uses CarrierActionAgent in construction so it needs to be created |
| // after CarrierActionAgent. |
| mCarrierActionAgent = mTelephonyComponentFactory.inject(CarrierActionAgent.class.getName()) |
| .makeCarrierActionAgent(this); |
| mCarrierSignalAgent = mTelephonyComponentFactory.inject(CarrierSignalAgent.class.getName()) |
| .makeCarrierSignalAgent(this); |
| mAccessNetworksManager = mTelephonyComponentFactory |
| .inject(AccessNetworksManager.class.getName()) |
| .makeAccessNetworksManager(this, getLooper()); |
| // SST/DSM depends on SSC, so SSC is instanced before SST/DSM |
| mSignalStrengthController = mTelephonyComponentFactory.inject( |
| SignalStrengthController.class.getName()).makeSignalStrengthController(this); |
| mSST = mTelephonyComponentFactory.inject(ServiceStateTracker.class.getName()) |
| .makeServiceStateTracker(this, this.mCi, featureFlags); |
| if (hasCalling()) { |
| mEmergencyNumberTracker = mTelephonyComponentFactory |
| .inject(EmergencyNumberTracker.class.getName()).makeEmergencyNumberTracker( |
| this, this.mCi, mFeatureFlags); |
| } |
| mDeviceStateMonitor = mTelephonyComponentFactory.inject(DeviceStateMonitor.class.getName()) |
| .makeDeviceStateMonitor(this, mFeatureFlags); |
| |
| // DisplayInfoController creates an OverrideNetworkTypeController, which uses |
| // DeviceStateMonitor so needs to be crated after it is instantiated. |
| mDisplayInfoController = mTelephonyComponentFactory.inject( |
| DisplayInfoController.class.getName()) |
| .makeDisplayInfoController(this, featureFlags); |
| |
| mDataNetworkController = mTelephonyComponentFactory.inject( |
| DataNetworkController.class.getName()) |
| .makeDataNetworkController(this, getLooper(), featureFlags); |
| |
| mCarrierResolver = mTelephonyComponentFactory.inject(CarrierResolver.class.getName()) |
| .makeCarrierResolver(this); |
| mCarrierPrivilegesTracker = new CarrierPrivilegesTracker(Looper.myLooper(), this, context); |
| |
| getCarrierActionAgent().registerForCarrierAction( |
| CarrierActionAgent.CARRIER_ACTION_SET_METERED_APNS_ENABLED, this, |
| EVENT_SET_CARRIER_DATA_ENABLED, null, false); |
| |
| mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null); |
| mSST.registerForVoiceRegStateOrRatChanged(this, EVENT_VRS_OR_RAT_CHANGED, null); |
| mSST.getServiceStateStats().registerDataNetworkControllerCallback(); |
| |
| mSubscriptionManagerService.registerCallback(new SubscriptionManagerServiceCallback( |
| this::post) { |
| @Override |
| public void onUiccApplicationsEnabledChanged(int subId) { |
| reapplyUiccAppsEnablementIfNeeded(ENABLE_UICC_APPS_MAX_RETRIES); |
| } |
| }); |
| |
| mLinkBandwidthEstimator = mTelephonyComponentFactory |
| .inject(LinkBandwidthEstimator.class.getName()) |
| .makeLinkBandwidthEstimator(this, getLooper()); |
| |
| mCallWaitingController = new CallWaitingController(this); |
| |
| if (hasCalling()) { |
| loadTtyMode(); |
| |
| CallManager.getInstance().registerPhone(this); |
| } |
| |
| mSubscriptionsChangedListener = |
| new SubscriptionManager.OnSubscriptionsChangedListener() { |
| @Override |
| public void onSubscriptionsChanged() { |
| sendEmptyMessage(EVENT_SUBSCRIPTIONS_CHANGED); |
| } |
| }; |
| |
| SubscriptionManager subMan = context.getSystemService(SubscriptionManager.class); |
| subMan.addOnSubscriptionsChangedListener( |
| new HandlerExecutor(this), mSubscriptionsChangedListener); |
| |
| logd("GsmCdmaPhone: constructor: sub = " + mPhoneId); |
| } |
| |
| private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| Rlog.d(LOG_TAG, "mBroadcastReceiver: action " + intent.getAction()); |
| String action = intent.getAction(); |
| if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) { |
| // Only handle carrier config changes for this phone id. |
| if (mPhoneId == intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, -1)) { |
| sendMessage(obtainMessage(EVENT_CARRIER_CONFIG_CHANGED)); |
| } |
| } else if (TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED.equals(action)) { |
| int ttyMode = intent.getIntExtra( |
| TelecomManager.EXTRA_CURRENT_TTY_MODE, TelecomManager.TTY_MODE_OFF); |
| updateTtyMode(ttyMode); |
| } else if (TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED.equals(action)) { |
| int newPreferredTtyMode = intent.getIntExtra( |
| TelecomManager.EXTRA_TTY_PREFERRED_MODE, TelecomManager.TTY_MODE_OFF); |
| updateUiTtyMode(newPreferredTtyMode); |
| } else if (TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED.equals(action)) { |
| if (mPhoneId == intent.getIntExtra( |
| SubscriptionManager.EXTRA_SLOT_INDEX, |
| SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { |
| mSimState = intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE, |
| TelephonyManager.SIM_STATE_UNKNOWN); |
| if (mSimState == TelephonyManager.SIM_STATE_LOADED |
| && currentSlotSubIdChanged()) { |
| setNetworkSelectionModeAutomatic(null); |
| } |
| } |
| } |
| } |
| }; |
| |
| private boolean hasCalling() { |
| if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true; |
| return mContext.getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_TELEPHONY_CALLING); |
| } |
| |
| private void initOnce(CommandsInterface ci) { |
| if (ci instanceof SimulatedRadioControl) { |
| mSimulatedRadioControl = (SimulatedRadioControl) ci; |
| } |
| |
| if (hasCalling()) { |
| mCT = mTelephonyComponentFactory.inject(GsmCdmaCallTracker.class.getName()) |
| .makeGsmCdmaCallTracker(this, mFeatureFlags); |
| } |
| mIccPhoneBookIntManager = mTelephonyComponentFactory |
| .inject(IccPhoneBookInterfaceManager.class.getName()) |
| .makeIccPhoneBookInterfaceManager(this); |
| PowerManager pm |
| = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); |
| mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); |
| mIccSmsInterfaceManager = mTelephonyComponentFactory |
| .inject(IccSmsInterfaceManager.class.getName()) |
| .makeIccSmsInterfaceManager(this, mFeatureFlags); |
| |
| mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); |
| mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); |
| mCi.registerForOn(this, EVENT_RADIO_ON, null); |
| mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); |
| mCi.registerUiccApplicationEnablementChanged(this, |
| EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED, |
| null); |
| mCi.setOnSuppServiceNotification(this, EVENT_SSN, null); |
| mCi.setOnRegistrationFailed(this, EVENT_REGISTRATION_FAILED, null); |
| mCi.registerForBarringInfoChanged(this, EVENT_BARRING_INFO_CHANGED, null); |
| |
| //GSM |
| mCi.setOnUSSD(this, EVENT_USSD, null); |
| mCi.setOnSs(this, EVENT_SS, null); |
| |
| //CDMA |
| mCdmaSSM = mTelephonyComponentFactory.inject(CdmaSubscriptionSourceManager.class.getName()) |
| .getCdmaSubscriptionSourceManagerInstance(mContext, |
| mCi, this, EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null); |
| mCi.setEmergencyCallbackMode(this, EVENT_EMERGENCY_CALLBACK_MODE_ENTER, null); |
| mCi.registerForExitEmergencyCallbackMode(this, EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE, |
| null); |
| mCi.registerForModemReset(this, EVENT_MODEM_RESET, null); |
| // get the string that specifies the carrier OTA Sp number |
| mCarrierOtaSpNumSchema = TelephonyManager.from(mContext).getOtaSpNumberSchemaForPhone( |
| getPhoneId(), ""); |
| |
| mResetModemOnRadioTechnologyChange = TelephonyProperties.reset_on_radio_tech_change() |
| .orElse(false); |
| |
| mCi.registerForRilConnected(this, EVENT_RIL_CONNECTED, null); |
| mCi.registerForVoiceRadioTechChanged(this, EVENT_VOICE_RADIO_TECH_CHANGED, null); |
| mCi.registerForLceInfo(this, EVENT_LINK_CAPACITY_CHANGED, null); |
| mCi.registerForCarrierInfoForImsiEncryption(this, |
| EVENT_RESET_CARRIER_KEY_IMSI_ENCRYPTION, null); |
| mCi.registerForTriggerImsDeregistration(this, EVENT_IMS_DEREGISTRATION_TRIGGERED, null); |
| mCi.registerForNotifyAnbr(this, EVENT_TRIGGER_NOTIFY_ANBR, null); |
| IntentFilter filter = new IntentFilter( |
| CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); |
| filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED); |
| filter.addAction(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED); |
| filter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED); |
| mContext.registerReceiver(mBroadcastReceiver, filter, |
| android.Manifest.permission.MODIFY_PHONE_STATE, null, Context.RECEIVER_EXPORTED); |
| |
| mCDM = new CarrierKeyDownloadManager(this); |
| mCIM = new CarrierInfoManager(); |
| |
| mCi.registerForImeiMappingChanged(this, EVENT_IMEI_MAPPING_CHANGED, null); |
| |
| if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents() |
| || mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) { |
| mSafetySource = |
| mTelephonyComponentFactory.makeCellularNetworkSecuritySafetySource(mContext); |
| } |
| |
| if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()) { |
| logi( |
| "enable_identifier_disclosure_transparency_unsol_events is on. Registering for " |
| + "cellular identifier disclosures from phone " |
| + getPhoneId()); |
| mIdentifierDisclosureNotifier = |
| mTelephonyComponentFactory |
| .inject(CellularIdentifierDisclosureNotifier.class.getName()) |
| .makeIdentifierDisclosureNotifier(mSafetySource); |
| mCi.registerForCellularIdentifierDisclosures( |
| this, EVENT_CELL_IDENTIFIER_DISCLOSURE, null); |
| } |
| |
| if (mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) { |
| logi( |
| "enable_modem_cipher_transparency_unsol_events is on. Registering for security " |
| + "algorithm updates from phone " |
| + getPhoneId()); |
| mNullCipherNotifier = |
| mTelephonyComponentFactory |
| .inject(NullCipherNotifier.class.getName()) |
| .makeNullCipherNotifier(mSafetySource); |
| mCi.registerForSecurityAlgorithmUpdates( |
| this, EVENT_SECURITY_ALGORITHM_UPDATE, null); |
| } |
| |
| initializeCarrierApps(); |
| } |
| |
| private void initRatSpecific(int precisePhoneType) { |
| mPendingMMIs.clear(); |
| mIccPhoneBookIntManager.updateIccRecords(null); |
| |
| mPrecisePhoneType = precisePhoneType; |
| logd("Precise phone type " + mPrecisePhoneType); |
| |
| TelephonyManager tm = TelephonyManager.from(mContext); |
| UiccProfile uiccProfile = getUiccProfile(); |
| if (isPhoneTypeGsm()) { |
| mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM); |
| tm.setPhoneType(getPhoneId(), PhoneConstants.PHONE_TYPE_GSM); |
| if (uiccProfile != null) { |
| uiccProfile.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS); |
| } |
| } else { |
| mCdmaSubscriptionSource = mCdmaSSM.getCdmaSubscriptionSource(); |
| // This is needed to handle phone process crashes |
| mIsPhoneInEcmState = getInEcmMode(); |
| if (mIsPhoneInEcmState) { |
| if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { |
| EmergencyStateTracker.getInstance().exitEmergencyCallbackMode(); |
| } else { |
| // Send a message which will invoke handleExitEmergencyCallbackMode |
| mCi.exitEmergencyCallbackMode(null); |
| } |
| } |
| |
| mCi.setPhoneType(PhoneConstants.PHONE_TYPE_CDMA); |
| tm.setPhoneType(getPhoneId(), PhoneConstants.PHONE_TYPE_CDMA); |
| if (uiccProfile != null) { |
| uiccProfile.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT); |
| } |
| // Sets operator properties by retrieving from build-time system property |
| String operatorAlpha = SystemProperties.get("ro.cdma.home.operator.alpha"); |
| String operatorNumeric = SystemProperties.get(PROPERTY_CDMA_HOME_OPERATOR_NUMERIC); |
| logd("init: operatorAlpha='" + operatorAlpha |
| + "' operatorNumeric='" + operatorNumeric + "'"); |
| if (!TextUtils.isEmpty(operatorAlpha)) { |
| logd("init: set 'gsm.sim.operator.alpha' to operator='" + operatorAlpha + "'"); |
| tm.setSimOperatorNameForPhone(mPhoneId, operatorAlpha); |
| } |
| if (!TextUtils.isEmpty(operatorNumeric)) { |
| logd("init: set 'gsm.sim.operator.numeric' to operator='" + operatorNumeric + |
| "'"); |
| logd("update icc_operator_numeric=" + operatorNumeric); |
| tm.setSimOperatorNumericForPhone(mPhoneId, operatorNumeric); |
| |
| mSubscriptionManagerService.setMccMnc(getSubId(), operatorNumeric); |
| |
| // Sets iso country property by retrieving from build-time system property |
| String iso = ""; |
| try { |
| iso = MccTable.countryCodeForMcc(operatorNumeric.substring(0, 3)); |
| } catch (StringIndexOutOfBoundsException ex) { |
| Rlog.e(LOG_TAG, "init: countryCodeForMcc error", ex); |
| } |
| |
| logd("init: set 'gsm.sim.operator.iso-country' to iso=" + iso); |
| tm.setSimCountryIsoForPhone(mPhoneId, iso); |
| mSubscriptionManagerService.setCountryIso(getSubId(), iso); |
| |
| // Updates MCC MNC device configuration information |
| logd("update mccmnc=" + operatorNumeric); |
| MccTable.updateMccMncConfiguration(mContext, operatorNumeric); |
| } |
| |
| // Sets current entry in the telephony carrier table |
| updateCurrentCarrierInProvider(operatorNumeric); |
| } |
| } |
| |
| /** |
| * Initialize the carrier apps. |
| */ |
| private void initializeCarrierApps() { |
| // Only perform on the default phone. There is no need to do it twice on the DSDS device. |
| if (mPhoneId != 0) return; |
| |
| logd("initializeCarrierApps"); |
| mContext.registerReceiverForAllUsers(new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| // Remove this line after testing |
| if (Intent.ACTION_USER_FOREGROUND.equals(intent.getAction())) { |
| UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER); |
| // If couldn't get current user ID, guess it's 0. |
| CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), |
| TelephonyManager.getDefault(), |
| userHandle != null ? userHandle.getIdentifier() : 0, mContext); |
| } |
| } |
| }, new IntentFilter(Intent.ACTION_USER_FOREGROUND), null, null); |
| CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), |
| TelephonyManager.getDefault(), ActivityManager.getCurrentUser(), mContext); |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public boolean isPhoneTypeGsm() { |
| return mPrecisePhoneType == PhoneConstants.PHONE_TYPE_GSM; |
| } |
| |
| public boolean isPhoneTypeCdma() { |
| return mPrecisePhoneType == PhoneConstants.PHONE_TYPE_CDMA; |
| } |
| |
| public boolean isPhoneTypeCdmaLte() { |
| return mPrecisePhoneType == PhoneConstants.PHONE_TYPE_CDMA_LTE; |
| } |
| |
| private void switchPhoneType(int precisePhoneType) { |
| removeCallbacks(mExitEcmRunnable); |
| |
| initRatSpecific(precisePhoneType); |
| |
| mSST.updatePhoneType(); |
| setPhoneName(precisePhoneType == PhoneConstants.PHONE_TYPE_GSM ? "GSM" : "CDMA"); |
| onUpdateIccAvailability(); |
| // if is possible that onUpdateIccAvailability() does not unregister and re-register for |
| // ICC events, for example if mUiccApplication does not change which can happen if phone |
| // type is transitioning from CDMA to GSM but 3gpp2 application was not available. |
| // To handle such cases, unregister and re-register here. They still need to be called in |
| // onUpdateIccAvailability(), since in normal cases register/unregister calls can be on |
| // different IccRecords objects. Here they are on the same IccRecords object. |
| unregisterForIccRecordEvents(); |
| registerForIccRecordEvents(); |
| |
| if (mCT != null) mCT.updatePhoneType(); |
| |
| int radioState = mCi.getRadioState(); |
| if (radioState != TelephonyManager.RADIO_POWER_UNAVAILABLE) { |
| handleRadioAvailable(); |
| if (radioState == TelephonyManager.RADIO_POWER_ON) { |
| handleRadioOn(); |
| } |
| } |
| if (radioState != TelephonyManager.RADIO_POWER_ON) { |
| handleRadioOffOrNotAvailable(); |
| } |
| } |
| |
| private void updateLinkCapacityEstimate(List<LinkCapacityEstimate> linkCapacityEstimateList) { |
| if (DBG) logd("updateLinkCapacityEstimate: lce list=" + linkCapacityEstimateList); |
| if (linkCapacityEstimateList == null) { |
| return; |
| } |
| notifyLinkCapacityEstimateChanged(linkCapacityEstimateList); |
| } |
| |
| @Override |
| protected void finalize() { |
| if(DBG) logd("GsmCdmaPhone finalized"); |
| if (mWakeLock != null && mWakeLock.isHeld()) { |
| Rlog.e(LOG_TAG, "UNEXPECTED; mWakeLock is held when finalizing."); |
| mWakeLock.release(); |
| } |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @Override |
| @NonNull |
| public ServiceState getServiceState() { |
| ServiceState baseSs = mSST != null ? mSST.getServiceState() : new ServiceState(); |
| ServiceState imsSs = mImsPhone != null ? mImsPhone.getServiceState() : new ServiceState(); |
| return mergeVoiceServiceStates(baseSs, imsSs, mTelecomVoiceServiceStateOverride); |
| } |
| |
| @Override |
| public void setVoiceServiceStateOverride(boolean hasService) { |
| int newOverride = |
| hasService ? ServiceState.STATE_IN_SERVICE : ServiceState.STATE_OUT_OF_SERVICE; |
| boolean changed = newOverride != mTelecomVoiceServiceStateOverride; |
| mTelecomVoiceServiceStateOverride = newOverride; |
| if (changed && mSST != null) { |
| mSST.onTelecomVoiceServiceStateOverrideChanged(); |
| mSST.getServiceStateStats().onVoiceServiceStateOverrideChanged(hasService); |
| } |
| } |
| |
| @Override |
| public void getCellIdentity(WorkSource workSource, Message rspMsg) { |
| mSST.requestCellIdentity(workSource, rspMsg); |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @Override |
| public PhoneConstants.State getState() { |
| if (!hasCalling()) return PhoneConstants.State.IDLE; |
| |
| if (mImsPhone != null) { |
| PhoneConstants.State imsState = mImsPhone.getState(); |
| if (imsState != PhoneConstants.State.IDLE) { |
| return imsState; |
| } |
| } |
| |
| return mCT.mState; |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @Override |
| public int getPhoneType() { |
| if (mPrecisePhoneType == PhoneConstants.PHONE_TYPE_GSM) { |
| return PhoneConstants.PHONE_TYPE_GSM; |
| } else { |
| return PhoneConstants.PHONE_TYPE_CDMA; |
| } |
| } |
| |
| @Override |
| public ServiceStateTracker getServiceStateTracker() { |
| return mSST; |
| } |
| |
| @Override |
| public EmergencyNumberTracker getEmergencyNumberTracker() { |
| return mEmergencyNumberTracker; |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @Override |
| public CallTracker getCallTracker() { |
| return mCT; |
| } |
| |
| @Override |
| public AccessNetworksManager getAccessNetworksManager() { |
| return mAccessNetworksManager; |
| } |
| |
| @Override |
| public DeviceStateMonitor getDeviceStateMonitor() { |
| return mDeviceStateMonitor; |
| } |
| |
| @Override |
| public DisplayInfoController getDisplayInfoController() { |
| return mDisplayInfoController; |
| } |
| |
| @Override |
| public SignalStrengthController getSignalStrengthController() { |
| return mSignalStrengthController; |
| } |
| |
| @Override |
| public void updateVoiceMail() { |
| if (isPhoneTypeGsm()) { |
| int countVoiceMessages = 0; |
| IccRecords r = mIccRecords.get(); |
| if (r != null) { |
| // get voice mail count from SIM |
| countVoiceMessages = r.getVoiceMessageCount(); |
| } |
| if (countVoiceMessages == IccRecords.DEFAULT_VOICE_MESSAGE_COUNT) { |
| countVoiceMessages = getStoredVoiceMessageCount(); |
| } |
| logd("updateVoiceMail countVoiceMessages = " + countVoiceMessages |
| + " subId " + getSubId()); |
| setVoiceMessageCount(countVoiceMessages); |
| } else { |
| setVoiceMessageCount(getStoredVoiceMessageCount()); |
| } |
| } |
| |
| @Override |
| public List<? extends MmiCode> |
| getPendingMmiCodes() { |
| return mPendingMMIs; |
| } |
| |
| @Override |
| public boolean isDataSuspended() { |
| if (mCT == null) return false; |
| return mCT.mState != PhoneConstants.State.IDLE && !mSST.isConcurrentVoiceAndDataAllowed(); |
| } |
| |
| @Override |
| public @DataActivityType int getDataActivityState() { |
| return getDataNetworkController().getDataActivity(); |
| } |
| |
| /** |
| * Notify any interested party of a Phone state change |
| * {@link com.android.internal.telephony.PhoneConstants.State} |
| */ |
| public void notifyPhoneStateChanged() { |
| mNotifier.notifyPhoneState(this); |
| } |
| |
| /** |
| * Notify registrants of a change in the call state. This notifies changes in |
| * {@link com.android.internal.telephony.Call.State}. Use this when changes |
| * in the precise call state are needed, else use notifyPhoneStateChanged. |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public void notifyPreciseCallStateChanged() { |
| /* we'd love it if this was package-scoped*/ |
| AsyncResult ar = new AsyncResult(null, this, null); |
| mPreciseCallStateRegistrants.notifyRegistrants(ar); |
| |
| mNotifier.notifyPreciseCallState(this, null, null, null); |
| } |
| |
| public void notifyNewRingingConnection(Connection c) { |
| super.notifyNewRingingConnectionP(c); |
| } |
| |
| public void notifyDisconnect(Connection cn) { |
| mDisconnectRegistrants.notifyResult(cn); |
| |
| mNotifier.notifyDisconnectCause(this, cn.getDisconnectCause(), |
| cn.getPreciseDisconnectCause()); |
| } |
| |
| public void notifyUnknownConnection(Connection cn) { |
| super.notifyUnknownConnectionP(cn); |
| } |
| |
| @Override |
| public boolean isInEmergencyCall() { |
| if (!hasCalling() || isPhoneTypeGsm()) { |
| return false; |
| } else { |
| return mCT.isInEmergencyCall(); |
| } |
| } |
| |
| @Override |
| protected void setIsInEmergencyCall() { |
| if (!hasCalling() && !isPhoneTypeGsm()) { |
| mCT.setIsInEmergencyCall(); |
| } |
| } |
| |
| @Override |
| public boolean isInEmergencySmsMode() { |
| return super.isInEmergencySmsMode() |
| || (mImsPhone != null && mImsPhone.isInEmergencySmsMode()); |
| } |
| |
| //CDMA |
| private void sendEmergencyCallbackModeChange(){ |
| //Send an Intent |
| Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); |
| intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, isInEcm()); |
| SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId()); |
| mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); |
| logi("sendEmergencyCallbackModeChange"); |
| } |
| |
| @Override |
| public void sendEmergencyCallStateChange(boolean callActive) { |
| if (!isPhoneTypeCdma()) { |
| // It possible that this method got called from ImsPhoneCallTracker# |
| logi("sendEmergencyCallStateChange - skip for non-cdma"); |
| return; |
| } |
| if (mBroadcastEmergencyCallStateChanges) { |
| Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED); |
| intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, callActive); |
| SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId()); |
| mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); |
| if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallStateChange: callActive " + callActive); |
| } |
| } |
| |
| @Override |
| public void setBroadcastEmergencyCallStateChanges(boolean broadcast) { |
| mBroadcastEmergencyCallStateChanges = broadcast; |
| } |
| |
| public void notifySuppServiceFailed(SuppService code) { |
| mSuppServiceFailedRegistrants.notifyResult(code); |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public void notifyServiceStateChanged(ServiceState ss) { |
| super.notifyServiceStateChangedP(ss); |
| } |
| |
| void notifyServiceStateChangedForSubId(ServiceState ss, int subId) { |
| super.notifyServiceStateChangedPForSubId(ss, subId); |
| } |
| |
| /** |
| * Notify that the cell location has changed. |
| * |
| * @param cellIdentity the new CellIdentity |
| */ |
| public void notifyLocationChanged(CellIdentity cellIdentity) { |
| mNotifier.notifyCellLocation(this, cellIdentity); |
| } |
| |
| @Override |
| public void notifyCallForwardingIndicator() { |
| mNotifier.notifyCallForwardingChanged(this); |
| } |
| |
| @Override |
| public void registerForSuppServiceNotification( |
| Handler h, int what, Object obj) { |
| mSsnRegistrants.addUnique(h, what, obj); |
| } |
| |
| @Override |
| public void unregisterForSuppServiceNotification(Handler h) { |
| mSsnRegistrants.remove(h); |
| } |
| |
| @Override |
| public void registerForSimRecordsLoaded(Handler h, int what, Object obj) { |
| mSimRecordsLoadedRegistrants.addUnique(h, what, obj); |
| } |
| |
| @Override |
| public void unregisterForSimRecordsLoaded(Handler h) { |
| mSimRecordsLoadedRegistrants.remove(h); |
| } |
| |
| @Override |
| public void acceptCall(int videoState) throws CallStateException { |
| if (!hasCalling()) throw new CallStateException(); |
| Phone imsPhone = mImsPhone; |
| if ( imsPhone != null && imsPhone.getRingingCall().isRinging() ) { |
| imsPhone.acceptCall(videoState); |
| } else { |
| mCT.acceptCall(); |
| } |
| } |
| |
| @Override |
| public void rejectCall() throws CallStateException { |
| if (!hasCalling()) throw new CallStateException(); |
| mCT.rejectCall(); |
| } |
| |
| @Override |
| public void switchHoldingAndActive() throws CallStateException { |
| mCT.switchWaitingOrHoldingAndActive(); |
| } |
| |
| @Override |
| public String getIccSerialNumber() { |
| IccRecords r = mIccRecords.get(); |
| if (!isPhoneTypeGsm() && r == null) { |
| // to get ICCID form SIMRecords because it is on MF. |
| r = mUiccController.getIccRecords(mPhoneId, UiccController.APP_FAM_3GPP); |
| } |
| return (r != null) ? r.getIccId() : null; |
| } |
| |
| @Override |
| public String getFullIccSerialNumber() { |
| IccRecords r = mIccRecords.get(); |
| if (!isPhoneTypeGsm() && r == null) { |
| // to get ICCID form SIMRecords because it is on MF. |
| r = mUiccController.getIccRecords(mPhoneId, UiccController.APP_FAM_3GPP); |
| } |
| return (r != null) ? r.getFullIccId() : null; |
| } |
| |
| @Override |
| public boolean canConference() { |
| if (!hasCalling()) return false; |
| if (mImsPhone != null && mImsPhone.canConference()) { |
| return true; |
| } |
| if (isPhoneTypeGsm()) { |
| return mCT.canConference(); |
| } else { |
| loge("canConference: not possible in CDMA"); |
| return false; |
| } |
| } |
| |
| @Override |
| public void conference() { |
| if (mImsPhone != null && mImsPhone.canConference()) { |
| logd("conference() - delegated to IMS phone"); |
| try { |
| mImsPhone.conference(); |
| } catch (CallStateException e) { |
| loge(e.toString()); |
| } |
| return; |
| } |
| if (isPhoneTypeGsm()) { |
| mCT.conference(); |
| } else { |
| // three way calls in CDMA will be handled by feature codes |
| loge("conference: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| loge("enableEnhancedVoicePrivacy: not expected on GSM"); |
| } else { |
| mCi.setPreferredVoicePrivacy(enable, onComplete); |
| } |
| } |
| |
| @Override |
| public void getEnhancedVoicePrivacy(Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| loge("getEnhancedVoicePrivacy: not expected on GSM"); |
| } else { |
| mCi.getPreferredVoicePrivacy(onComplete); |
| } |
| } |
| |
| @Override |
| public void clearDisconnected() { |
| if (!hasCalling()) return; |
| mCT.clearDisconnected(); |
| } |
| |
| @Override |
| public boolean canTransfer() { |
| if (hasCalling() && isPhoneTypeGsm()) { |
| return mCT.canTransfer(); |
| } else { |
| loge("canTransfer: not possible in CDMA"); |
| return false; |
| } |
| } |
| |
| @Override |
| public void explicitCallTransfer() { |
| if (hasCalling() && isPhoneTypeGsm()) { |
| mCT.explicitCallTransfer(); |
| } else { |
| loge("explicitCallTransfer: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public GsmCdmaCall getForegroundCall() { |
| return mCT.mForegroundCall; |
| } |
| |
| @Override |
| public GsmCdmaCall getBackgroundCall() { |
| if (!hasCalling()) return null; |
| return mCT.mBackgroundCall; |
| } |
| |
| @Override |
| public Call getRingingCall() { |
| if (!hasCalling()) return null; |
| Phone imsPhone = mImsPhone; |
| // It returns the ringing call of ImsPhone if the ringing call of GSMPhone isn't ringing. |
| // In CallManager.registerPhone(), it always registers ringing call of ImsPhone, because |
| // the ringing call of GSMPhone isn't ringing. Consequently, it can't answer GSM call |
| // successfully by invoking TelephonyManager.answerRingingCall() since the implementation |
| // in PhoneInterfaceManager.answerRingingCallInternal() could not get the correct ringing |
| // call from CallManager. So we check the ringing call state of imsPhone first as |
| // accpetCall() does. |
| if ( imsPhone != null && imsPhone.getRingingCall().isRinging()) { |
| return imsPhone.getRingingCall(); |
| } |
| //It returns the ringing connections which during SRVCC handover |
| if (!mCT.mRingingCall.isRinging() |
| && mCT.getRingingHandoverConnection() != null |
| && mCT.getRingingHandoverConnection().getCall() != null |
| && mCT.getRingingHandoverConnection().getCall().isRinging()) { |
| return mCT.getRingingHandoverConnection().getCall(); |
| } |
| return mCT.mRingingCall; |
| } |
| |
| @Override |
| @NonNull |
| public CarrierPrivilegesTracker getCarrierPrivilegesTracker() { |
| return mCarrierPrivilegesTracker; |
| } |
| |
| /** |
| * Amends {@code baseSs} if its voice registration state is {@code OUT_OF_SERVICE}. |
| * |
| * <p>Even if the device has lost the CS link to the tower, there are two potential additional |
| * sources of voice capability not directly saved inside ServiceStateTracker: |
| * |
| * <ul> |
| * <li>IMS voice registration state ({@code imsSs}) - if this is {@code IN_SERVICE} for voice, |
| * we substite {@code baseSs#getDataRegState} as the final voice service state (ImsService |
| * reports {@code IN_SERVICE} for its voice registration state even if the device has lost |
| * the physical link to the tower) |
| * <li>OTT voice capability provided through telecom ({@code telecomSs}) - if this is {@code |
| * IN_SERVICE}, we directly substitute it as the final voice service state |
| * </ul> |
| */ |
| private static ServiceState mergeVoiceServiceStates( |
| ServiceState baseSs, ServiceState imsSs, @ServiceState.RegState int telecomSs) { |
| if (baseSs.getState() == ServiceState.STATE_IN_SERVICE) { |
| // No need to merge states if the baseSs is IN_SERVICE. |
| return baseSs; |
| } |
| // If any of the following additional sources are IN_SERVICE, we use that since voice calls |
| // can be routed through something other than the CS link. |
| @ServiceState.RegState int finalVoiceSs = ServiceState.STATE_OUT_OF_SERVICE; |
| if (telecomSs == ServiceState.STATE_IN_SERVICE) { |
| // If telecom reports there's a PhoneAccount that can provide voice service |
| // (CAPABILITY_VOICE_CALLING_AVAILABLE), then we trust that info as it may account for |
| // external possibilities like wi-fi calling provided by the SIM call manager app. Note |
| // that CAPABILITY_PLACE_EMERGENCY_CALLS is handled separately. |
| finalVoiceSs = telecomSs; |
| } else if (imsSs.getState() == ServiceState.STATE_IN_SERVICE) { |
| // Voice override for IMS case. In this case, voice registration is OUT_OF_SERVICE, but |
| // IMS is available, so use data registration state as a basis for determining |
| // whether or not the physical link is available. |
| finalVoiceSs = baseSs.getDataRegistrationState(); |
| } |
| if (finalVoiceSs != ServiceState.STATE_IN_SERVICE) { |
| // None of the additional sources provide a usable route, and they only use IN/OUT. |
| return baseSs; |
| } |
| ServiceState newSs = new ServiceState(baseSs); |
| newSs.setVoiceRegState(finalVoiceSs); |
| newSs.setEmergencyOnly(false); // Must be IN_SERVICE if we're here |
| return newSs; |
| } |
| |
| private boolean handleCallDeflectionIncallSupplementaryService( |
| String dialString) { |
| if (!hasCalling() || dialString.length() > 1) { |
| return false; |
| } |
| |
| if (getRingingCall().getState() != GsmCdmaCall.State.IDLE) { |
| if (DBG) logd("MmiCode 0: rejectCall"); |
| try { |
| mCT.rejectCall(); |
| } catch (CallStateException e) { |
| if (DBG) Rlog.d(LOG_TAG, |
| "reject failed", e); |
| notifySuppServiceFailed(Phone.SuppService.REJECT); |
| } |
| } else if (getBackgroundCall().getState() != GsmCdmaCall.State.IDLE) { |
| if (DBG) logd("MmiCode 0: hangupWaitingOrBackground"); |
| mCT.hangupWaitingOrBackground(); |
| } |
| |
| return true; |
| } |
| |
| //GSM |
| private boolean handleCallWaitingIncallSupplementaryService(String dialString) { |
| int len = dialString.length(); |
| |
| if (!hasCalling() || len > 2) { |
| return false; |
| } |
| |
| GsmCdmaCall call = getForegroundCall(); |
| |
| try { |
| if (len > 1) { |
| char ch = dialString.charAt(1); |
| int callIndex = ch - '0'; |
| |
| if (callIndex >= 1 && callIndex <= GsmCdmaCallTracker.MAX_CONNECTIONS_GSM) { |
| if (DBG) logd("MmiCode 1: hangupConnectionByIndex " + callIndex); |
| mCT.hangupConnectionByIndex(call, callIndex); |
| } |
| } else { |
| if (call.getState() != GsmCdmaCall.State.IDLE) { |
| if (DBG) logd("MmiCode 1: hangup foreground"); |
| //mCT.hangupForegroundResumeBackground(); |
| mCT.hangup(call); |
| } else { |
| if (DBG) logd("MmiCode 1: switchWaitingOrHoldingAndActive"); |
| mCT.switchWaitingOrHoldingAndActive(); |
| } |
| } |
| } catch (CallStateException e) { |
| if (DBG) Rlog.d(LOG_TAG, |
| "hangup failed", e); |
| notifySuppServiceFailed(Phone.SuppService.HANGUP); |
| } |
| |
| return true; |
| } |
| |
| private boolean handleCallHoldIncallSupplementaryService(String dialString) { |
| int len = dialString.length(); |
| |
| if (len > 2) { |
| return false; |
| } |
| |
| GsmCdmaCall call = getForegroundCall(); |
| |
| if (len > 1) { |
| try { |
| char ch = dialString.charAt(1); |
| int callIndex = ch - '0'; |
| GsmCdmaConnection conn = mCT.getConnectionByIndex(call, callIndex); |
| |
| // GsmCdma index starts at 1, up to 5 connections in a call, |
| if (conn != null && callIndex >= 1 && callIndex <= GsmCdmaCallTracker.MAX_CONNECTIONS_GSM) { |
| if (DBG) logd("MmiCode 2: separate call " + callIndex); |
| mCT.separate(conn); |
| } else { |
| if (DBG) logd("separate: invalid call index " + callIndex); |
| notifySuppServiceFailed(Phone.SuppService.SEPARATE); |
| } |
| } catch (CallStateException e) { |
| if (DBG) Rlog.d(LOG_TAG, "separate failed", e); |
| notifySuppServiceFailed(Phone.SuppService.SEPARATE); |
| } |
| } else { |
| try { |
| if (getRingingCall().getState() != GsmCdmaCall.State.IDLE) { |
| if (DBG) logd("MmiCode 2: accept ringing call"); |
| mCT.acceptCall(); |
| } else { |
| if (DBG) logd("MmiCode 2: switchWaitingOrHoldingAndActive"); |
| mCT.switchWaitingOrHoldingAndActive(); |
| } |
| } catch (CallStateException e) { |
| if (DBG) Rlog.d(LOG_TAG, "switch failed", e); |
| notifySuppServiceFailed(Phone.SuppService.SWITCH); |
| } |
| } |
| |
| return true; |
| } |
| |
| private boolean handleMultipartyIncallSupplementaryService(String dialString) { |
| if (dialString.length() > 1) { |
| return false; |
| } |
| |
| if (DBG) logd("MmiCode 3: merge calls"); |
| conference(); |
| return true; |
| } |
| |
| private boolean handleEctIncallSupplementaryService(String dialString) { |
| |
| int len = dialString.length(); |
| |
| if (len != 1) { |
| return false; |
| } |
| |
| if (DBG) logd("MmiCode 4: explicit call transfer"); |
| explicitCallTransfer(); |
| return true; |
| } |
| |
| private boolean handleCcbsIncallSupplementaryService(String dialString) { |
| if (dialString.length() > 1) { |
| return false; |
| } |
| |
| Rlog.i(LOG_TAG, "MmiCode 5: CCBS not supported!"); |
| // Treat it as an "unknown" service. |
| notifySuppServiceFailed(Phone.SuppService.UNKNOWN); |
| return true; |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @Override |
| public boolean handleInCallMmiCommands(String dialString) throws CallStateException { |
| if (!isPhoneTypeGsm()) { |
| loge("method handleInCallMmiCommands is NOT supported in CDMA!"); |
| return false; |
| } |
| |
| Phone imsPhone = mImsPhone; |
| if (imsPhone != null |
| && imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) { |
| return imsPhone.handleInCallMmiCommands(dialString); |
| } |
| |
| if (!isInCall()) { |
| return false; |
| } |
| |
| if (TextUtils.isEmpty(dialString)) { |
| return false; |
| } |
| |
| boolean result = false; |
| char ch = dialString.charAt(0); |
| switch (ch) { |
| case '0': |
| result = handleCallDeflectionIncallSupplementaryService(dialString); |
| break; |
| case '1': |
| result = handleCallWaitingIncallSupplementaryService(dialString); |
| break; |
| case '2': |
| result = handleCallHoldIncallSupplementaryService(dialString); |
| break; |
| case '3': |
| result = handleMultipartyIncallSupplementaryService(dialString); |
| break; |
| case '4': |
| result = handleEctIncallSupplementaryService(dialString); |
| break; |
| case '5': |
| result = handleCcbsIncallSupplementaryService(dialString); |
| break; |
| default: |
| break; |
| } |
| |
| return result; |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public boolean isInCall() { |
| GsmCdmaCall.State foregroundCallState = getForegroundCall().getState(); |
| GsmCdmaCall.State backgroundCallState = getBackgroundCall().getState(); |
| GsmCdmaCall.State ringingCallState = getRingingCall().getState(); |
| |
| return (foregroundCallState.isAlive() || |
| backgroundCallState.isAlive() || |
| ringingCallState.isAlive()); |
| } |
| |
| private boolean useImsForCall(DialArgs dialArgs) { |
| return isImsUseEnabled() |
| && mImsPhone != null |
| && (mImsPhone.isVoiceOverCellularImsEnabled() || mImsPhone.isWifiCallingEnabled() |
| || (mImsPhone.isVideoEnabled() && VideoProfile.isVideo(dialArgs.videoState))) |
| && (mImsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE); |
| } |
| |
| public boolean useImsForEmergency() { |
| CarrierConfigManager configManager = |
| (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| boolean alwaysTryImsForEmergencyCarrierConfig = configManager.getConfigForSubId(getSubId()) |
| .getBoolean(CarrierConfigManager.KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL); |
| return mImsPhone != null |
| && alwaysTryImsForEmergencyCarrierConfig |
| && ImsManager.getInstance(mContext, mPhoneId).isNonTtyOrTtyOnVolteEnabled() |
| && mImsPhone.isImsAvailable(); |
| } |
| |
| @Override |
| public Connection startConference(String[] participantsToDial, DialArgs dialArgs) |
| throws CallStateException { |
| Phone imsPhone = mImsPhone; |
| boolean useImsForCall = useImsForCall(dialArgs); |
| logd("useImsForCall=" + useImsForCall); |
| if (useImsForCall) { |
| try { |
| if (DBG) logd("Trying IMS PS Conference call"); |
| return imsPhone.startConference(participantsToDial, dialArgs); |
| } catch (CallStateException e) { |
| if (DBG) logd("IMS PS conference call exception " + e + |
| "useImsForCall =" + useImsForCall + ", imsPhone =" + imsPhone); |
| CallStateException ce = new CallStateException(e.getError(), e.getMessage()); |
| ce.setStackTrace(e.getStackTrace()); |
| throw ce; |
| } |
| } else { |
| throw new CallStateException( |
| CallStateException.ERROR_OUT_OF_SERVICE, |
| "cannot dial conference call in out of service"); |
| } |
| } |
| |
| @Override |
| public Connection dial(String dialString, @NonNull DialArgs dialArgs, |
| Consumer<Phone> chosenPhoneConsumer) throws CallStateException { |
| if (!hasCalling()) { |
| throw new CallStateException("Calling feature is not supported!"); |
| } |
| if (!isPhoneTypeGsm() && dialArgs.uusInfo != null) { |
| throw new CallStateException("Sending UUS information NOT supported in CDMA!"); |
| } |
| String possibleEmergencyNumber = checkForTestEmergencyNumber(dialString); |
| // Record if the dialed number was swapped for a test emergency number. |
| boolean isDialedNumberSwapped = !TextUtils.equals(dialString, possibleEmergencyNumber); |
| if (isDialedNumberSwapped) { |
| logi("dialString replaced for possible emergency number: " + dialString + " -> " |
| + possibleEmergencyNumber); |
| dialString = possibleEmergencyNumber; |
| } |
| |
| CarrierConfigManager configManager = |
| (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle carrierConfig = configManager.getConfigForSubId(getSubId()); |
| boolean allowWpsOverIms = carrierConfig.getBoolean( |
| CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL); |
| boolean useOnlyDialedSimEccList = carrierConfig.getBoolean( |
| CarrierConfigManager.KEY_USE_ONLY_DIALED_SIM_ECC_LIST_BOOL); |
| |
| |
| TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); |
| boolean isEmergency; |
| // Check if the carrier wants to treat a call as an emergency call based on its own list of |
| // known emergency numbers. |
| // useOnlyDialedSimEccList is false for the vast majority of carriers. There are, however, |
| // some carriers which do not want to handle dial requests for numbers which are in the |
| // emergency number list on another SIM, but is not on theirs. In this case we will use the |
| // emergency number list for this carrier's SIM only. |
| if (useOnlyDialedSimEccList) { |
| isEmergency = getEmergencyNumberTracker().isEmergencyNumber(dialString); |
| logi("dial; isEmergency=" + isEmergency |
| + " (based on this phone only); globalIsEmergency=" |
| + tm.isEmergencyNumber(dialString)); |
| } else { |
| isEmergency = tm.isEmergencyNumber(dialString); |
| logi("dial; isEmergency=" + isEmergency + " (based on all phones)"); |
| } |
| |
| // Undetectable emergeny number indicated by new domain selection service |
| if (dialArgs.isEmergency) { |
| logi("dial; isEmergency=" + isEmergency + " (domain selection module)"); |
| isEmergency = true; |
| } |
| |
| /** Check if the call is Wireless Priority Service call */ |
| boolean isWpsCall = PhoneNumberUtils.isWpsCallNumber(dialString); |
| |
| ImsPhone.ImsDialArgs.Builder imsDialArgsBuilder; |
| imsDialArgsBuilder = ImsPhone.ImsDialArgs.Builder.from(dialArgs) |
| .setIsEmergency(isEmergency) |
| .setIsWpsCall(isWpsCall); |
| mDialArgs = dialArgs = imsDialArgsBuilder.build(); |
| |
| Phone imsPhone = mImsPhone; |
| |
| boolean useImsForEmergency = isEmergency && useImsForEmergency(); |
| |
| String dialPart = PhoneNumberUtils.extractNetworkPortionAlt(PhoneNumberUtils. |
| stripSeparators(dialString)); |
| boolean isMmiCode = (dialPart.startsWith("*") || dialPart.startsWith("#")) |
| && dialPart.endsWith("#"); |
| boolean isSuppServiceCode = ImsPhoneMmiCode.isSuppServiceCodes(dialPart, this); |
| boolean isPotentialUssdCode = isMmiCode && !isSuppServiceCode; |
| boolean useImsForUt = imsPhone != null && imsPhone.isUtEnabled(); |
| boolean useImsForCall = useImsForCall(dialArgs) |
| && (isWpsCall ? allowWpsOverIms : true); |
| |
| Bundle extras = dialArgs.intentExtras; |
| // Only when the domain selection service is supported, EXTRA_DIAL_DOMAIN extra shall exist. |
| if (extras != null && extras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN)) { |
| int domain = extras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN); |
| logi("dial domain=" + domain); |
| useImsForCall = false; |
| useImsForUt = false; |
| useImsForEmergency = false; |
| if (domain == DOMAIN_PS) { |
| if (isEmergency) { |
| useImsForEmergency = true; |
| } else if (!isMmiCode || isPotentialUssdCode) { |
| useImsForCall = true; |
| } else { |
| // should not reach here |
| loge("dial unexpected Ut domain selection, ignored"); |
| } |
| } else if (domain == PhoneConstants.DOMAIN_NON_3GPP_PS) { |
| if (isEmergency) { |
| useImsForEmergency = true; |
| extras.putString(ImsCallProfile.EXTRA_CALL_RAT_TYPE, |
| String.valueOf(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)); |
| } else { |
| // should not reach here |
| loge("dial DOMAIN_NON_3GPP_PS should be used only for emergency calls"); |
| } |
| } |
| |
| extras.remove(PhoneConstants.EXTRA_DIAL_DOMAIN); |
| } |
| |
| if (DBG) { |
| logi("useImsForCall=" + useImsForCall |
| + ", useOnlyDialedSimEccList=" + useOnlyDialedSimEccList |
| + ", isEmergency=" + isEmergency |
| + ", useImsForEmergency=" + useImsForEmergency |
| + ", useImsForUt=" + useImsForUt |
| + ", isUt=" + isMmiCode |
| + ", isSuppServiceCode=" + isSuppServiceCode |
| + ", isPotentialUssdCode=" + isPotentialUssdCode |
| + ", isWpsCall=" + isWpsCall |
| + ", allowWpsOverIms=" + allowWpsOverIms |
| + ", imsPhone=" + imsPhone |
| + ", imsPhone.isVoiceOverCellularImsEnabled()=" |
| + ((imsPhone != null) ? imsPhone.isVoiceOverCellularImsEnabled() : "N/A") |
| + ", imsPhone.isVowifiEnabled()=" |
| + ((imsPhone != null) ? imsPhone.isWifiCallingEnabled() : "N/A") |
| + ", imsPhone.isVideoEnabled()=" |
| + ((imsPhone != null) ? imsPhone.isVideoEnabled() : "N/A") |
| + ", imsPhone.getServiceState().getState()=" |
| + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A")); |
| } |
| |
| // Perform FDN check for non-emergency calls - shouldn't dial if number is blocked by FDN |
| if(!isEmergency && FdnUtils.isNumberBlockedByFDN(mPhoneId, dialString, getCountryIso())) { |
| throw new CallStateException(CallStateException.ERROR_FDN_BLOCKED, |
| "cannot dial number blocked by FDN"); |
| } |
| |
| // Bypass WiFi Only WFC check if this is an emergency call - we should still try to |
| // place over cellular if possible. |
| if (!isEmergency) { |
| Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mPhoneId, mContext); |
| } |
| if (imsPhone != null && !allowWpsOverIms && !useImsForCall && isWpsCall |
| && imsPhone.getCallTracker() instanceof ImsPhoneCallTracker) { |
| logi("WPS call placed over CS; disconnecting all IMS calls.."); |
| ImsPhoneCallTracker tracker = (ImsPhoneCallTracker) imsPhone.getCallTracker(); |
| tracker.hangupAllConnections(); |
| } |
| |
| if ((useImsForCall && (!isMmiCode || isPotentialUssdCode)) |
| || (isMmiCode && useImsForUt) |
| || useImsForEmergency) { |
| try { |
| if (DBG) logd("Trying IMS PS call"); |
| chosenPhoneConsumer.accept(imsPhone); |
| return imsPhone.dial(dialString, dialArgs); |
| } catch (CallStateException e) { |
| if (DBG) logd("IMS PS call exception " + e + |
| "useImsForCall =" + useImsForCall + ", imsPhone =" + imsPhone); |
| // Do not throw a CallStateException and instead fall back to Circuit switch |
| // for emergency calls and MMI codes. |
| if (Phone.CS_FALLBACK.equals(e.getMessage()) || isEmergency) { |
| logi("IMS call failed with Exception: " + e.getMessage() + ". Falling back " |
| + "to CS."); |
| } else { |
| CallStateException ce = new CallStateException(e.getError(), e.getMessage()); |
| ce.setStackTrace(e.getStackTrace()); |
| throw ce; |
| } |
| } |
| } |
| |
| if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE |
| && mSST.mSS.getDataRegistrationState() != ServiceState.STATE_IN_SERVICE |
| && !isEmergency) { |
| throw new CallStateException("cannot dial in current state"); |
| } |
| // Check non-emergency voice CS call - shouldn't dial when POWER_OFF |
| if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_POWER_OFF /* CS POWER_OFF */ |
| && !VideoProfile.isVideo(dialArgs.videoState) /* voice call */ |
| && !isEmergency /* non-emergency call */ |
| && !(isMmiCode && useImsForUt) /* not UT */ |
| /* If config_allow_ussd_over_ims is false, USSD is sent over the CS pipe instead */ |
| && !isPotentialUssdCode) { |
| throw new CallStateException( |
| CallStateException.ERROR_POWER_OFF, |
| "cannot dial voice call in airplane mode"); |
| } |
| // Check for service before placing non emergency CS voice call. |
| // Allow dial only if either CS is camped on any RAT (or) PS is in LTE/NR service. |
| if (mSST != null |
| && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE /* CS out of service */ |
| && !(mSST.mSS.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE |
| && ServiceState.isPsOnlyTech( |
| mSST.mSS.getRilDataRadioTechnology())) /* PS not in LTE/NR */ |
| && !VideoProfile.isVideo(dialArgs.videoState) /* voice call */ |
| && !isEmergency /* non-emergency call */ |
| /* If config_allow_ussd_over_ims is false, USSD is sent over the CS pipe instead */ |
| && !isPotentialUssdCode) { |
| throw new CallStateException( |
| CallStateException.ERROR_OUT_OF_SERVICE, |
| "cannot dial voice call in out of service"); |
| } |
| if (DBG) logd("Trying (non-IMS) CS call"); |
| if (isDialedNumberSwapped && isEmergency) { |
| // If domain selection is enabled, ECM testing is handled in EmergencyStateTracker |
| if (!DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { |
| // Triggers ECM when CS call ends only for test emergency calls using |
| // ril.test.emergencynumber. |
| mIsTestingEmergencyCallbackMode = true; |
| mCi.testingEmergencyCall(); |
| } |
| } |
| |
| chosenPhoneConsumer.accept(this); |
| return dialInternal(dialString, dialArgs); |
| } |
| |
| /** |
| * @return {@code true} if the user should be informed of an attempt to dial an international |
| * number while on WFC only, {@code false} otherwise. |
| */ |
| public boolean isNotificationOfWfcCallRequired(String dialString) { |
| CarrierConfigManager configManager = |
| (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle config = configManager.getConfigForSubId(getSubId()); |
| |
| // Determine if carrier config indicates that international calls over WFC should trigger a |
| // notification to the user. This is controlled by carrier configuration and is off by |
| // default. |
| boolean shouldNotifyInternationalCallOnWfc = config != null |
| && config.getBoolean( |
| CarrierConfigManager.KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL); |
| |
| if (!shouldNotifyInternationalCallOnWfc) { |
| return false; |
| } |
| |
| Phone imsPhone = mImsPhone; |
| TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); |
| boolean isEmergency = tm.isEmergencyNumber(dialString); |
| boolean shouldConfirmCall = |
| // Using IMS |
| isImsUseEnabled() |
| && imsPhone != null |
| // VoLTE not available |
| && !imsPhone.isVoiceOverCellularImsEnabled() |
| // WFC is available |
| && imsPhone.isWifiCallingEnabled() |
| && !isEmergency |
| // Dialing international number |
| && PhoneNumberUtils.isInternationalNumber(dialString, getCountryIso()); |
| return shouldConfirmCall; |
| } |
| |
| @Override |
| protected Connection dialInternal(String dialString, DialArgs dialArgs) |
| throws CallStateException { |
| return dialInternal(dialString, dialArgs, null); |
| } |
| |
| protected Connection dialInternal(String dialString, DialArgs dialArgs, |
| ResultReceiver wrappedCallback) |
| throws CallStateException { |
| |
| // Need to make sure dialString gets parsed properly |
| String newDialString = PhoneNumberUtils.stripSeparators(dialString); |
| |
| if (isPhoneTypeGsm()) { |
| // handle in-call MMI first if applicable |
| if (handleInCallMmiCommands(newDialString)) { |
| return null; |
| } |
| |
| // Only look at the Network portion for mmi |
| String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString); |
| GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this, |
| mUiccApplication.get(), wrappedCallback); |
| if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'..."); |
| |
| if (mmi == null) { |
| return mCT.dialGsm(newDialString, dialArgs); |
| } else if (mmi.isTemporaryModeCLIR()) { |
| return mCT.dialGsm(mmi.mDialingNumber, mmi.getCLIRMode(), dialArgs.uusInfo, |
| dialArgs.intentExtras); |
| } else { |
| mPendingMMIs.add(mmi); |
| mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); |
| mmi.processCode(); |
| return null; |
| } |
| } else { |
| return mCT.dial(newDialString, dialArgs); |
| } |
| } |
| |
| @Override |
| public boolean handlePinMmi(String dialString) { |
| MmiCode mmi; |
| if (isPhoneTypeGsm()) { |
| mmi = GsmMmiCode.newFromDialString(dialString, this, mUiccApplication.get()); |
| } else { |
| mmi = CdmaMmiCode.newFromDialString(dialString, this, mUiccApplication.get()); |
| } |
| |
| if (mmi != null && mmi.isPinPukCommand()) { |
| mPendingMMIs.add(mmi); |
| mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); |
| try { |
| mmi.processCode(); |
| } catch (CallStateException e) { |
| //do nothing |
| } |
| return true; |
| } |
| |
| loge("Mmi is null or unrecognized!"); |
| return false; |
| } |
| |
| private void sendUssdResponse(String ussdRequest, CharSequence message, int returnCode, |
| ResultReceiver wrappedCallback) { |
| UssdResponse response = new UssdResponse(ussdRequest, message); |
| Bundle returnData = new Bundle(); |
| returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response); |
| wrappedCallback.send(returnCode, returnData); |
| } |
| |
| @Override |
| public boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback) { |
| if (!isPhoneTypeGsm() || mPendingMMIs.size() > 0) { |
| //todo: replace the generic failure with specific error code. |
| sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE, |
| wrappedCallback ); |
| return true; |
| } |
| |
| // Perform FDN check |
| if(FdnUtils.isNumberBlockedByFDN(mPhoneId, ussdRequest, getCountryIso())) { |
| sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE, |
| wrappedCallback ); |
| return true; |
| } |
| |
| // Try over IMS if possible. |
| Phone imsPhone = mImsPhone; |
| if ((imsPhone != null) |
| && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) |
| || imsPhone.isUtEnabled())) { |
| try { |
| logd("handleUssdRequest: attempting over IMS"); |
| return imsPhone.handleUssdRequest(ussdRequest, wrappedCallback); |
| } catch (CallStateException cse) { |
| if (!CS_FALLBACK.equals(cse.getMessage())) { |
| return false; |
| } |
| // At this point we've tried over IMS but have been informed we need to handover |
| // back to GSM. |
| logd("handleUssdRequest: fallback to CS required"); |
| } |
| } |
| |
| // Try USSD over GSM. |
| try { |
| dialInternal(ussdRequest, new DialArgs.Builder<>().build(), wrappedCallback); |
| } catch (Exception e) { |
| logd("handleUssdRequest: exception" + e); |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public void sendUssdResponse(String ussdMessge) { |
| if (isPhoneTypeGsm()) { |
| GsmMmiCode mmi = GsmMmiCode.newFromUssdUserInput(ussdMessge, this, mUiccApplication.get()); |
| mPendingMMIs.add(mmi); |
| mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); |
| mmi.sendUssd(ussdMessge); |
| } else { |
| loge("sendUssdResponse: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public void sendDtmf(char c) { |
| if (!PhoneNumberUtils.is12Key(c)) { |
| loge("sendDtmf called with invalid character '" + c + "'"); |
| } else { |
| if (mCT.mState == PhoneConstants.State.OFFHOOK) { |
| mCi.sendDtmf(c, null); |
| } |
| } |
| } |
| |
| @Override |
| public void startDtmf(char c) { |
| if (!PhoneNumberUtils.is12Key(c)) { |
| loge("startDtmf called with invalid character '" + c + "'"); |
| } else { |
| mCi.startDtmf(c, null); |
| } |
| } |
| |
| @Override |
| public void stopDtmf() { |
| mCi.stopDtmf(null); |
| } |
| |
| @Override |
| public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| loge("[GsmCdmaPhone] sendBurstDtmf() is a CDMA method"); |
| } else { |
| boolean check = true; |
| for (int itr = 0;itr < dtmfString.length(); itr++) { |
| if (!PhoneNumberUtils.is12Key(dtmfString.charAt(itr))) { |
| Rlog.e( |
| LOG_TAG, |
| "sendDtmf called with invalid character '" |
| + dtmfString.charAt(itr) |
| + "'"); |
| check = false; |
| break; |
| } |
| } |
| if (mCT.mState == PhoneConstants.State.OFFHOOK && check) { |
| mCi.sendBurstDtmf(dtmfString, on, off, onComplete); |
| } |
| } |
| } |
| |
| @Override |
| public void setRadioPowerOnForTestEmergencyCall(boolean isSelectedPhoneForEmergencyCall) { |
| mSST.clearAllRadioOffReasons(); |
| |
| // We don't want to have forEmergency call be true to prevent radio emergencyDial command |
| // from being called for a test emergency number because the network may not be able to |
| // find emergency routing for it and dial it do the default emergency services line. |
| setRadioPower(true, false, isSelectedPhoneForEmergencyCall, false); |
| } |
| |
| @Override |
| public void setRadioPower(boolean power, boolean forEmergencyCall, |
| boolean isSelectedPhoneForEmergencyCall, boolean forceApply) { |
| setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply, |
| TelephonyManager.RADIO_POWER_REASON_USER); |
| } |
| |
| @Override |
| public void setRadioPowerForReason(boolean power, boolean forEmergencyCall, |
| boolean isSelectedPhoneForEmergencyCall, boolean forceApply, int reason) { |
| mSST.setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, |
| forceApply, reason); |
| } |
| |
| @Override |
| public Set<Integer> getRadioPowerOffReasons() { |
| return mSST.getRadioPowerOffReasons(); |
| } |
| |
| private void storeVoiceMailNumber(String number) { |
| SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); |
| SharedPreferences.Editor editor = sp.edit(); |
| setVmSimImsi(getSubscriberId()); |
| logd("storeVoiceMailNumber: mPrecisePhoneType=" + mPrecisePhoneType + " vmNumber=" |
| + Rlog.pii(LOG_TAG, number)); |
| if (isPhoneTypeGsm()) { |
| editor.putString(VM_NUMBER + getPhoneId(), number); |
| editor.apply(); |
| } else { |
| editor.putString(VM_NUMBER_CDMA + getPhoneId(), number); |
| editor.apply(); |
| } |
| } |
| |
| @Override |
| public String getVoiceMailNumber() { |
| String number = null; |
| if (isPhoneTypeGsm() || mSimRecords != null) { |
| // Read from the SIM. If its null, try reading from the shared preference area. |
| IccRecords r = isPhoneTypeGsm() ? mIccRecords.get() : mSimRecords; |
| number = (r != null) ? r.getVoiceMailNumber() : ""; |
| if (TextUtils.isEmpty(number)) { |
| SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); |
| String spName = isPhoneTypeGsm() ? VM_NUMBER : VM_NUMBER_CDMA; |
| number = sp.getString(spName + getPhoneId(), null); |
| logd("getVoiceMailNumber: from " + spName + " number=" |
| + Rlog.piiHandle(number)); |
| } else { |
| logd("getVoiceMailNumber: from IccRecords number=" + Rlog.piiHandle(number)); |
| } |
| } |
| if (!isPhoneTypeGsm() && TextUtils.isEmpty(number)) { |
| SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); |
| number = sp.getString(VM_NUMBER_CDMA + getPhoneId(), null); |
| logd("getVoiceMailNumber: from VM_NUMBER_CDMA number=" + Rlog.piiHandle(number)); |
| } |
| |
| if (TextUtils.isEmpty(number)) { |
| CarrierConfigManager configManager = (CarrierConfigManager) |
| getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle b = configManager.getConfigForSubId(getSubId()); |
| if (b != null) { |
| String defaultVmNumber = |
| b.getString(CarrierConfigManager.KEY_DEFAULT_VM_NUMBER_STRING); |
| String defaultVmNumberRoaming = |
| b.getString(CarrierConfigManager.KEY_DEFAULT_VM_NUMBER_ROAMING_STRING); |
| String defaultVmNumberRoamingAndImsUnregistered = b.getString( |
| CarrierConfigManager |
| .KEY_DEFAULT_VM_NUMBER_ROAMING_AND_IMS_UNREGISTERED_STRING); |
| |
| if (!TextUtils.isEmpty(defaultVmNumber)) number = defaultVmNumber; |
| if (mSST.mSS.getRoaming()) { |
| if (!TextUtils.isEmpty(defaultVmNumberRoamingAndImsUnregistered) |
| && !mSST.isImsRegistered()) { |
| // roaming and IMS unregistered case if CC configured |
| number = defaultVmNumberRoamingAndImsUnregistered; |
| logd("getVoiceMailNumber: from defaultVmNumberRoamingAndImsUnregistered " |
| + "number=" + Rlog.piiHandle(number)); |
| } else if (!TextUtils.isEmpty(defaultVmNumberRoaming)) { |
| // roaming default case if CC configured |
| number = defaultVmNumberRoaming; |
| logd("getVoiceMailNumber: from defaultVmNumberRoaming number=" + |
| Rlog.piiHandle(number)); |
| } |
| } |
| } |
| } |
| |
| if (TextUtils.isEmpty(number)) { |
| // Read platform settings for dynamic voicemail number |
| CarrierConfigManager configManager = (CarrierConfigManager) |
| getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle b = configManager.getConfigForSubId(getSubId()); |
| if (b != null && b.getBoolean( |
| CarrierConfigManager.KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL)) { |
| number = getLine1Number(); |
| logd("getVoiceMailNumber: from MSISDN number=" + Rlog.piiHandle(number)); |
| } |
| } |
| return number; |
| } |
| |
| |
| private String getVmSimImsi() { |
| SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); |
| return sp.getString(VM_SIM_IMSI + getPhoneId(), null); |
| } |
| |
| private void setVmSimImsi(String imsi) { |
| SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); |
| SharedPreferences.Editor editor = sp.edit(); |
| editor.putString(VM_SIM_IMSI + getPhoneId(), imsi); |
| editor.apply(); |
| } |
| |
| @Override |
| public String getVoiceMailAlphaTag() { |
| String ret = ""; |
| |
| if (isPhoneTypeGsm() || mSimRecords != null) { |
| IccRecords r = isPhoneTypeGsm() ? mIccRecords.get() : mSimRecords; |
| |
| ret = (r != null) ? r.getVoiceMailAlphaTag() : ""; |
| } |
| |
| if (ret == null || ret.length() == 0) { |
| return mContext.getText( |
| com.android.internal.R.string.defaultVoiceMailAlphaTag).toString(); |
| } |
| |
| return ret; |
| } |
| |
| @Override |
| public String getDeviceId() { |
| if (isPhoneTypeGsm()) { |
| return mImei; |
| } else { |
| CarrierConfigManager configManager = (CarrierConfigManager) |
| mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| boolean force_imei = configManager.getConfigForSubId(getSubId()) |
| .getBoolean(CarrierConfigManager.KEY_FORCE_IMEI_BOOL); |
| if (force_imei) return mImei; |
| |
| String id = getMeid(); |
| if ((id == null) || id.matches("^0*$")) { |
| loge("getDeviceId(): MEID is not initialized use ESN"); |
| id = getEsn(); |
| } |
| return id; |
| } |
| } |
| |
| @Override |
| public String getDeviceSvn() { |
| if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) { |
| return mImeiSv; |
| } else { |
| loge("getDeviceSvn(): return 0"); |
| return "0"; |
| } |
| } |
| |
| @Override |
| public IsimRecords getIsimRecords() { |
| return mIsimUiccRecords; |
| } |
| |
| @Override |
| public String getImei() { |
| return mImei; |
| } |
| |
| @Override |
| public int getImeiType() { |
| return mImeiType; |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @Override |
| public String getEsn() { |
| if (isPhoneTypeGsm()) { |
| loge("[GsmCdmaPhone] getEsn() is a CDMA method"); |
| return "0"; |
| } else { |
| return mEsn; |
| } |
| } |
| |
| @Override |
| public String getMeid() { |
| return mMeid; |
| } |
| |
| @Override |
| public String getNai() { |
| IccRecords r = mUiccController.getIccRecords(mPhoneId, UiccController.APP_FAM_3GPP2); |
| if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { |
| Rlog.v(LOG_TAG, "IccRecords is " + r); |
| } |
| return (r != null) ? r.getNAI() : null; |
| } |
| |
| @Override |
| @Nullable |
| public String getSubscriberId() { |
| String subscriberId = null; |
| if (isPhoneTypeCdma()) { |
| subscriberId = mSST.getImsi(); |
| } else { |
| // Both Gsm and CdmaLte get the IMSI from Usim. |
| IccRecords iccRecords = mUiccController.getIccRecords( |
| mPhoneId, UiccController.APP_FAM_3GPP); |
| if (iccRecords != null) { |
| subscriberId = iccRecords.getIMSI(); |
| } |
| } |
| return subscriberId; |
| } |
| |
| @Override |
| public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType, boolean fallback) { |
| final TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class) |
| .createForSubscriptionId(getSubId()); |
| String operatorNumeric = telephonyManager.getSimOperator(); |
| int carrierId = telephonyManager.getSimCarrierId(); |
| return CarrierInfoManager.getCarrierInfoForImsiEncryption(keyType, |
| mContext, operatorNumeric, carrierId, fallback, getSubId()); |
| } |
| |
| @Override |
| public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) { |
| CarrierInfoManager.setCarrierInfoForImsiEncryption(imsiEncryptionInfo, mContext, mPhoneId); |
| mCi.setCarrierInfoForImsiEncryption(imsiEncryptionInfo, null); |
| } |
| |
| @Override |
| public void deleteCarrierInfoForImsiEncryption(int carrierId) { |
| CarrierInfoManager.deleteCarrierInfoForImsiEncryption(mContext, getSubId(), carrierId); |
| } |
| |
| @Override |
| public void deleteCarrierInfoForImsiEncryption(int carrierId, String simOperator) { |
| CarrierInfoManager.deleteCarrierInfoForImsiEncryption(mContext, getSubId(), |
| carrierId, simOperator); |
| } |
| |
| @Override |
| public int getCarrierId() { |
| return mCarrierResolver != null |
| ? mCarrierResolver.getCarrierId() : super.getCarrierId(); |
| } |
| |
| @Override |
| public String getCarrierName() { |
| return mCarrierResolver != null |
| ? mCarrierResolver.getCarrierName() : super.getCarrierName(); |
| } |
| |
| @Override |
| public int getMNOCarrierId() { |
| return mCarrierResolver != null |
| ? mCarrierResolver.getMnoCarrierId() : super.getMNOCarrierId(); |
| } |
| |
| @Override |
| public int getSpecificCarrierId() { |
| return mCarrierResolver != null |
| ? mCarrierResolver.getSpecificCarrierId() : super.getSpecificCarrierId(); |
| } |
| |
| @Override |
| public String getSpecificCarrierName() { |
| return mCarrierResolver != null |
| ? mCarrierResolver.getSpecificCarrierName() : super.getSpecificCarrierName(); |
| } |
| |
| @Override |
| public void resolveSubscriptionCarrierId(String simState) { |
| if (mCarrierResolver != null) { |
| mCarrierResolver.resolveSubscriptionCarrierId(simState); |
| } |
| } |
| |
| @Override |
| public int getCarrierIdListVersion() { |
| return mCarrierResolver != null |
| ? mCarrierResolver.getCarrierListVersion() : super.getCarrierIdListVersion(); |
| } |
| |
| @Override |
| public int getEmergencyNumberDbVersion() { |
| EmergencyNumberTracker tracker = getEmergencyNumberTracker(); |
| if (tracker == null) return -1; |
| return tracker.getEmergencyNumberDbVersion(); |
| } |
| |
| @Override |
| public void resetCarrierKeysForImsiEncryption() { |
| mCIM.resetCarrierKeysForImsiEncryption(mContext, mPhoneId); |
| } |
| |
| @Override |
| public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1, |
| String gid2, String pnn, String spn, String carrierPrivilegeRules, String apn) { |
| mCarrierResolver.setTestOverrideApn(apn); |
| UiccProfile uiccProfile = mUiccController.getUiccProfileForPhone(getPhoneId()); |
| if (uiccProfile != null) { |
| List<UiccAccessRule> testRules; |
| if (carrierPrivilegeRules == null) { |
| testRules = null; |
| } else if (carrierPrivilegeRules.isEmpty()) { |
| testRules = Collections.emptyList(); |
| } else { |
| UiccAccessRule accessRule = new UiccAccessRule( |
| IccUtils.hexStringToBytes(carrierPrivilegeRules), null, 0); |
| testRules = Collections.singletonList(accessRule); |
| } |
| uiccProfile.setTestOverrideCarrierPrivilegeRules(testRules); |
| } else { |
| // TODO: Fix "privilege" typo throughout telephony. |
| mCarrierResolver.setTestOverrideCarrierPriviledgeRule(carrierPrivilegeRules); // NOTYPO |
| } |
| IccRecords r = null; |
| if (isPhoneTypeGsm()) { |
| r = mIccRecords.get(); |
| } else if (isPhoneTypeCdmaLte()) { |
| r = mSimRecords; |
| } else { |
| loge("setCarrierTestOverride fails in CDMA only"); |
| } |
| if (r != null) { |
| r.setCarrierTestOverride(mccmnc, imsi, iccid, gid1, gid2, pnn, spn); |
| } |
| } |
| |
| @Override |
| public String getGroupIdLevel1() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.getGid1() : null; |
| } else if (isPhoneTypeCdma()) { |
| loge("GID1 is not available in CDMA"); |
| return null; |
| } else { //isPhoneTypeCdmaLte() |
| return (mSimRecords != null) ? mSimRecords.getGid1() : ""; |
| } |
| } |
| |
| @Override |
| public String getGroupIdLevel2() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.getGid2() : null; |
| } else if (isPhoneTypeCdma()) { |
| loge("GID2 is not available in CDMA"); |
| return null; |
| } else { //isPhoneTypeCdmaLte() |
| return (mSimRecords != null) ? mSimRecords.getGid2() : ""; |
| } |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @Override |
| public String getLine1Number() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.getMsisdnNumber() : null; |
| } else { |
| CarrierConfigManager configManager = (CarrierConfigManager) |
| mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| boolean use_usim = configManager.getConfigForSubId(getSubId()).getBoolean( |
| CarrierConfigManager.KEY_USE_USIM_BOOL); |
| if (use_usim) { |
| return (mSimRecords != null) ? mSimRecords.getMsisdnNumber() : null; |
| } |
| return mSST.getMdnNumber(); |
| } |
| } |
| |
| @Override |
| public String getPlmn() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.getPnnHomeName() : null; |
| } else if (isPhoneTypeCdma()) { |
| loge("Plmn is not available in CDMA"); |
| return null; |
| } else { //isPhoneTypeCdmaLte() |
| return (mSimRecords != null) ? mSimRecords.getPnnHomeName() : null; |
| } |
| } |
| |
| /** |
| * Update non-persisited manual network selection. |
| * |
| * @param nsm contains Plmn info |
| */ |
| @Override |
| protected void updateManualNetworkSelection(NetworkSelectMessage nsm) { |
| int subId = getSubId(); |
| if (SubscriptionManager.isValidSubscriptionId(subId)) { |
| mManualNetworkSelectionPlmn = nsm.operatorNumeric; |
| } else { |
| //on Phone0 in emergency mode (no SIM), or in some races then clear the cache |
| mManualNetworkSelectionPlmn = null; |
| Rlog.e(LOG_TAG, "Cannot update network selection due to invalid subId " |
| + subId); |
| } |
| } |
| |
| @Override |
| public String getManualNetworkSelectionPlmn() { |
| return (mManualNetworkSelectionPlmn == null) ? "" : mManualNetworkSelectionPlmn; |
| } |
| |
| @Override |
| protected void onSetNetworkSelectionModeCompleted() { |
| mSST.pollState(); |
| } |
| |
| @Override |
| public String getCdmaPrlVersion() { |
| return mSST.getPrlVersion(); |
| } |
| |
| @Override |
| public String getCdmaMin() { |
| return mSST.getCdmaMin(); |
| } |
| |
| @Override |
| public boolean isMinInfoReady() { |
| return mSST.isMinInfoReady(); |
| } |
| |
| @Override |
| public String getMsisdn() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.getMsisdnNumber() : null; |
| } else if (isPhoneTypeCdmaLte()) { |
| return (mSimRecords != null) ? mSimRecords.getMsisdnNumber() : null; |
| } else { |
| loge("getMsisdn: not expected on CDMA"); |
| return null; |
| } |
| } |
| |
| @Override |
| public String getLine1AlphaTag() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.getMsisdnAlphaTag() : null; |
| } else { |
| loge("getLine1AlphaTag: not possible in CDMA"); |
| return null; |
| } |
| } |
| |
| @Override |
| public boolean setLine1Number(String alphaTag, String number, Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| if (r != null) { |
| r.setMsisdnNumber(alphaTag, number, onComplete); |
| return true; |
| } else { |
| return false; |
| } |
| } else { |
| loge("setLine1Number: not possible in CDMA"); |
| return false; |
| } |
| } |
| |
| @Override |
| public void setVoiceMailNumber(String alphaTag, String voiceMailNumber, Message onComplete) { |
| Message resp; |
| mVmNumber = voiceMailNumber; |
| resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete); |
| |
| IccRecords r = mIccRecords.get(); |
| |
| if (!isPhoneTypeGsm() && mSimRecords != null) { |
| r = mSimRecords; |
| } |
| |
| if (r != null) { |
| r.setVoiceMailNumber(alphaTag, mVmNumber, resp); |
| } |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) { |
| switch (commandInterfaceCFReason) { |
| case CF_REASON_UNCONDITIONAL: |
| case CF_REASON_BUSY: |
| case CF_REASON_NO_REPLY: |
| case CF_REASON_NOT_REACHABLE: |
| case CF_REASON_ALL: |
| case CF_REASON_ALL_CONDITIONAL: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @Override |
| public String getSystemProperty(String property, String defValue) { |
| if (getUnitTestMode()) { |
| return null; |
| } |
| return TelephonyManager.getTelephonyProperty(mPhoneId, property, defValue); |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) { |
| switch (commandInterfaceCFAction) { |
| case CF_ACTION_DISABLE: |
| case CF_ACTION_ENABLE: |
| case CF_ACTION_REGISTRATION: |
| case CF_ACTION_ERASURE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private boolean isCfEnable(int action) { |
| return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION); |
| } |
| |
| private boolean isImsUtEnabledOverCdma() { |
| return isPhoneTypeCdmaLte() |
| && mImsPhone != null |
| && mImsPhone.isUtEnabled(); |
| } |
| |
| private boolean isCsRetry(Message onComplete) { |
| if (onComplete != null) { |
| return onComplete.getData().getBoolean(CS_FALLBACK_SS, false); |
| } |
| return false; |
| } |
| |
| private void updateSsOverCdmaSupported(@NonNull PersistableBundle b) { |
| mSsOverCdmaSupported = b.getBoolean(CarrierConfigManager.KEY_SUPPORT_SS_OVER_CDMA_BOOL); |
| } |
| |
| /** |
| * Enables or disables N1 mode (access to 5G core network) in accordance with |
| * 3GPP TS 24.501 4.9. |
| * |
| * <p> To prevent redundant calls down to the modem and to support a mechanism whereby |
| * N1 mode is only on if both IMS and carrier config believe that it should be on, this |
| * method will first sync the value from the modem prior to possibly setting it. In addition |
| * N1 mode will not be set to enabled unless both IMS and Carrier want it, since the use |
| * cases require all entities to agree lest it default to disabled. |
| * |
| * @param enable {@code true} to enable N1 mode, {@code false} to disable N1 mode. |
| * @param result Callback message to receive the result or null. |
| */ |
| @Override |
| public void setN1ModeEnabled(boolean enable, @Nullable Message result) { |
| if (mFeatureFlags.enableCarrierConfigN1ControlAttempt2()) { |
| // This might be called by IMS on another thread, so to avoid the requirement to |
| // lock, post it through the handler. |
| post(() -> { |
| if (enable) { |
| mN1ModeDisallowedReasons.remove(N1_MODE_DISALLOWED_REASON_IMS); |
| } else { |
| mN1ModeDisallowedReasons.add(N1_MODE_DISALLOWED_REASON_IMS); |
| } |
| if (mModemN1Mode == null) { |
| mCi.isN1ModeEnabled(obtainMessage(EVENT_GET_N1_MODE_ENABLED_DONE, result)); |
| } else { |
| maybeUpdateModemN1Mode(result); |
| } |
| }); |
| } else { |
| super.setN1ModeEnabled(enable, result); |
| } |
| } |
| |
| /** Only called on the handler thread. */ |
| private void maybeUpdateModemN1Mode(@Nullable Message result) { |
| final boolean wantN1Enabled = mN1ModeDisallowedReasons.isEmpty(); |
| |
| logd("N1 Mode: isModemN1Enabled=" + mModemN1Mode + ", wantN1Enabled=" + wantN1Enabled); |
| |
| // mModemN1Mode is never null here |
| if (mModemN1Mode != wantN1Enabled) { |
| // Assume success pending a response, which avoids multiple concurrent requests |
| // going down to the modem. If it fails, that is addressed in the response. |
| mModemN1Mode = wantN1Enabled; |
| super.setN1ModeEnabled( |
| wantN1Enabled, obtainMessage(EVENT_SET_N1_MODE_ENABLED_DONE, result)); |
| } else if (result != null) { |
| AsyncResult.forMessage(result); |
| result.sendToTarget(); |
| } |
| } |
| |
| /** Only called on the handler thread. */ |
| private void updateCarrierN1ModeSupported(@NonNull PersistableBundle b) { |
| if (!mFeatureFlags.enableCarrierConfigN1ControlAttempt2()) return; |
| |
| if (!CarrierConfigManager.isConfigForIdentifiedCarrier(b)) return; |
| |
| final int[] supportedNrModes = b.getIntArray( |
| CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY); |
| |
| |
| if (ArrayUtils.contains( |
| supportedNrModes, |
| CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA)) { |
| mN1ModeDisallowedReasons.remove(N1_MODE_DISALLOWED_REASON_CARRIER); |
| } else { |
| mN1ModeDisallowedReasons.add(N1_MODE_DISALLOWED_REASON_CARRIER); |
| } |
| |
| if (mModemN1Mode == null) { |
| mCi.isN1ModeEnabled(obtainMessage(EVENT_GET_N1_MODE_ENABLED_DONE)); |
| } else { |
| maybeUpdateModemN1Mode(null); |
| } |
| } |
| |
| @Override |
| public boolean useSsOverIms(Message onComplete) { |
| boolean isUtEnabled = isUtEnabled(); |
| |
| Rlog.d(LOG_TAG, "useSsOverIms: isUtEnabled()= " + isUtEnabled + |
| " isCsRetry(onComplete))= " + isCsRetry(onComplete)); |
| |
| if (isUtEnabled && !isCsRetry(onComplete)) { |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) { |
| getCallForwardingOption(commandInterfaceCFReason, |
| CommandsInterface.SERVICE_CLASS_VOICE, onComplete); |
| } |
| |
| @Override |
| public void getCallForwardingOption(int commandInterfaceCFReason, int serviceClass, |
| Message onComplete) { |
| // Perform FDN check |
| SsData.ServiceType serviceType = GsmMmiCode.cfReasonToServiceType(commandInterfaceCFReason); |
| if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, serviceType)) { |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); |
| onComplete.sendToTarget(); |
| return; |
| } |
| |
| Phone imsPhone = mImsPhone; |
| if (useSsOverIms(onComplete)) { |
| imsPhone.getCallForwardingOption(commandInterfaceCFReason, serviceClass, onComplete); |
| return; |
| } |
| |
| if (isPhoneTypeGsm()) { |
| if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) { |
| if (DBG) logd("requesting call forwarding query."); |
| Message resp; |
| if (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL) { |
| resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete); |
| } else { |
| resp = onComplete; |
| } |
| mCi.queryCallForwardStatus(commandInterfaceCFReason, serviceClass, null, resp); |
| } |
| } else { |
| if (!mSsOverCdmaSupported) { |
| // If SS over CDMA is not supported and UT is not at the time, notify the user of |
| // the error and disable the option. |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.INVALID_STATE, |
| "Call Forwarding over CDMA unavailable")); |
| } else { |
| loge("getCallForwardingOption: not possible in CDMA, just return empty result"); |
| AsyncResult.forMessage(onComplete, makeEmptyCallForward(), null); |
| } |
| onComplete.sendToTarget(); |
| } |
| } |
| |
| @Override |
| public void setCallForwardingOption(int commandInterfaceCFAction, |
| int commandInterfaceCFReason, |
| String dialingNumber, |
| int timerSeconds, |
| Message onComplete) { |
| setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason, |
| dialingNumber, CommandsInterface.SERVICE_CLASS_VOICE, timerSeconds, onComplete); |
| } |
| |
| @Override |
| public void setCallForwardingOption(int commandInterfaceCFAction, |
| int commandInterfaceCFReason, |
| String dialingNumber, |
| int serviceClass, |
| int timerSeconds, |
| Message onComplete) { |
| // Perform FDN check |
| SsData.RequestType requestType = GsmMmiCode.cfActionToRequestType(commandInterfaceCFAction); |
| SsData.ServiceType serviceType = GsmMmiCode.cfReasonToServiceType(commandInterfaceCFReason); |
| if(isRequestBlockedByFDN(requestType, serviceType)) { |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); |
| onComplete.sendToTarget(); |
| return; |
| } |
| |
| Phone imsPhone = mImsPhone; |
| if (useSsOverIms(onComplete)) { |
| imsPhone.setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason, |
| dialingNumber, serviceClass, timerSeconds, onComplete); |
| return; |
| } |
| |
| if (isPhoneTypeGsm()) { |
| if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) && |
| (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) { |
| |
| Message resp; |
| if (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL) { |
| Cfu cfu = new Cfu(dialingNumber, onComplete); |
| resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE, |
| isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, cfu); |
| } else { |
| resp = onComplete; |
| } |
| mCi.setCallForward(commandInterfaceCFAction, |
| commandInterfaceCFReason, |
| serviceClass, |
| dialingNumber, |
| timerSeconds, |
| resp); |
| } |
| } else if (mSsOverCdmaSupported) { |
| String formatNumber = GsmCdmaConnection.formatDialString(dialingNumber); |
| String cfNumber = CdmaMmiCode.getCallForwardingPrefixAndNumber( |
| commandInterfaceCFAction, commandInterfaceCFReason, formatNumber); |
| loge("setCallForwardingOption: dial for set call forwarding" |
| + " prefixWithNumber= " + cfNumber + " number= " + dialingNumber); |
| |
| PhoneAccountHandle phoneAccountHandle = subscriptionIdToPhoneAccountHandle(getSubId()); |
| Bundle extras = new Bundle(); |
| extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle); |
| |
| final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class); |
| telecomManager.placeCall( |
| Uri.fromParts(PhoneAccount.SCHEME_TEL, cfNumber, null), extras); |
| |
| AsyncResult.forMessage(onComplete, CommandsInterface.SS_STATUS_UNKNOWN, null); |
| onComplete.sendToTarget(); |
| } else { |
| loge("setCallForwardingOption: SS over CDMA not supported, can not complete"); |
| AsyncResult.forMessage(onComplete, CommandsInterface.SS_STATUS_UNKNOWN, null); |
| onComplete.sendToTarget(); |
| } |
| } |
| |
| @Override |
| public void getCallBarring(String facility, String password, Message onComplete, |
| int serviceClass) { |
| // Perform FDN check |
| SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility); |
| if (isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, serviceType)) { |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); |
| onComplete.sendToTarget(); |
| return; |
| } |
| |
| Phone imsPhone = mImsPhone; |
| if (useSsOverIms(onComplete)) { |
| imsPhone.getCallBarring(facility, password, onComplete, serviceClass); |
| return; |
| } |
| |
| if (isPhoneTypeGsm()) { |
| mCi.queryFacilityLock(facility, password, serviceClass, onComplete); |
| } else { |
| loge("getCallBarringOption: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public void setCallBarring(String facility, boolean lockState, String password, |
| Message onComplete, int serviceClass) { |
| // Perform FDN check |
| SsData.RequestType requestType = lockState ? SsData.RequestType.SS_ACTIVATION : |
| SsData.RequestType.SS_DEACTIVATION; |
| SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility); |
| if (isRequestBlockedByFDN(requestType, serviceType)) { |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); |
| onComplete.sendToTarget(); |
| return; |
| } |
| |
| Phone imsPhone = mImsPhone; |
| if (useSsOverIms(onComplete)) { |
| imsPhone.setCallBarring(facility, lockState, password, onComplete, serviceClass); |
| return; |
| } |
| |
| if (isPhoneTypeGsm()) { |
| mCi.setFacilityLock(facility, lockState, password, serviceClass, onComplete); |
| } else { |
| loge("setCallBarringOption: not possible in CDMA"); |
| } |
| } |
| |
| /** |
| * Changes access code used for call barring |
| * |
| * @param facility is one of CB_FACILTY_* |
| * @param oldPwd is old password |
| * @param newPwd is new password |
| * @param onComplete is callback message when the action is completed. |
| */ |
| public void changeCallBarringPassword(String facility, String oldPwd, String newPwd, |
| Message onComplete) { |
| // Perform FDN check |
| SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility); |
| ArrayList<String> controlStrings = GsmMmiCode.getControlStringsForPwd( |
| SsData.RequestType.SS_REGISTRATION, |
| serviceType); |
| if(FdnUtils.isSuppServiceRequestBlockedByFdn(mPhoneId, controlStrings, getCountryIso())) { |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); |
| onComplete.sendToTarget(); |
| return; |
| } |
| |
| if (isPhoneTypeGsm()) { |
| mCi.changeBarringPassword(facility, oldPwd, newPwd, onComplete); |
| } else { |
| loge("changeCallBarringPassword: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public void getOutgoingCallerIdDisplay(Message onComplete) { |
| // Perform FDN check |
| if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_CLIR)){ |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); |
| onComplete.sendToTarget(); |
| return; |
| } |
| |
| Phone imsPhone = mImsPhone; |
| |
| if (useSsOverIms(onComplete)) { |
| imsPhone.getOutgoingCallerIdDisplay(onComplete); |
| return; |
| } |
| |
| if (isPhoneTypeGsm()) { |
| mCi.getCLIR(onComplete); |
| } else { |
| loge("getOutgoingCallerIdDisplay: not possible in CDMA"); |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED)); |
| onComplete.sendToTarget(); |
| } |
| } |
| |
| @Override |
| public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) { |
| // Perform FDN check |
| SsData.RequestType requestType = GsmMmiCode.clirModeToRequestType(commandInterfaceCLIRMode); |
| if (isRequestBlockedByFDN(requestType, SsData.ServiceType.SS_CLIR)) { |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); |
| onComplete.sendToTarget(); |
| return; |
| } |
| |
| Phone imsPhone = mImsPhone; |
| if (useSsOverIms(onComplete)) { |
| imsPhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode, onComplete); |
| return; |
| } |
| |
| if (isPhoneTypeGsm()) { |
| // Packing CLIR value in the message. This will be required for |
| // SharedPreference caching, if the message comes back as part of |
| // a success response. |
| mCi.setCLIR(commandInterfaceCLIRMode, |
| obtainMessage(EVENT_SET_CLIR_COMPLETE, commandInterfaceCLIRMode, 0, onComplete)); |
| } else { |
| loge("setOutgoingCallerIdDisplay: not possible in CDMA"); |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED)); |
| onComplete.sendToTarget(); |
| } |
| } |
| |
| @Override |
| public void queryCLIP(Message onComplete) { |
| // Perform FDN check |
| if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_CLIP)){ |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); |
| onComplete.sendToTarget(); |
| return; |
| } |
| |
| Phone imsPhone = mImsPhone; |
| if (useSsOverIms(onComplete)) { |
| imsPhone.queryCLIP(onComplete); |
| return; |
| } |
| |
| if (isPhoneTypeGsm()) { |
| mCi.queryCLIP(onComplete); |
| } else { |
| loge("queryCLIP: not possible in CDMA"); |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED)); |
| onComplete.sendToTarget(); |
| } |
| } |
| |
| @Override |
| public void getCallWaiting(Message onComplete) { |
| // Perform FDN check |
| if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_WAIT)){ |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); |
| onComplete.sendToTarget(); |
| return; |
| } |
| |
| if (mCallWaitingController.getCallWaiting(onComplete)) return; |
| |
| Phone imsPhone = mImsPhone; |
| if (useSsOverIms(onComplete)) { |
| imsPhone.getCallWaiting(onComplete); |
| return; |
| } |
| |
| if (isPhoneTypeGsm()) { |
| //As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service |
| //class parameter in call waiting interrogation to network |
| mCi.queryCallWaiting(CommandsInterface.SERVICE_CLASS_NONE, onComplete); |
| } else { |
| if (!mSsOverCdmaSupported) { |
| // If SS over CDMA is not supported and UT is not at the time, notify the user of |
| // the error and disable the option. |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.INVALID_STATE, |
| "Call Waiting over CDMA unavailable")); |
| } else { |
| int[] arr = |
| {CommandsInterface.SS_STATUS_UNKNOWN, CommandsInterface.SERVICE_CLASS_NONE}; |
| AsyncResult.forMessage(onComplete, arr, null); |
| } |
| onComplete.sendToTarget(); |
| } |
| } |
| |
| @Override |
| public void setCallWaiting(boolean enable, Message onComplete) { |
| int serviceClass = CommandsInterface.SERVICE_CLASS_VOICE; |
| CarrierConfigManager configManager = (CarrierConfigManager) |
| getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle b = configManager.getConfigForSubId(getSubId()); |
| if (b != null) { |
| serviceClass = b.getInt(CarrierConfigManager.KEY_CALL_WAITING_SERVICE_CLASS_INT, |
| CommandsInterface.SERVICE_CLASS_VOICE); |
| } |
| setCallWaiting(enable, serviceClass, onComplete); |
| } |
| |
| @Override |
| public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) { |
| // Perform FDN check |
| SsData.RequestType requestType = enable ? SsData.RequestType.SS_ACTIVATION : |
| SsData.RequestType.SS_DEACTIVATION; |
| if (isRequestBlockedByFDN(requestType, SsData.ServiceType.SS_WAIT)) { |
| AsyncResult.forMessage(onComplete, null, |
| new CommandException(CommandException.Error.FDN_CHECK_FAILURE)); |
| onComplete.sendToTarget(); |
| return; |
| } |
| |
| if (mCallWaitingController.setCallWaiting(enable, serviceClass, onComplete)) return; |
| |
| Phone imsPhone = mImsPhone; |
| if (useSsOverIms(onComplete)) { |
| imsPhone.setCallWaiting(enable, onComplete); |
| return; |
| } |
| |
| if (isPhoneTypeGsm()) { |
| mCi.setCallWaiting(enable, serviceClass, onComplete); |
| } else if (mSsOverCdmaSupported) { |
| String cwPrefix = CdmaMmiCode.getCallWaitingPrefix(enable); |
| Rlog.i( |
| LOG_TAG, |
| "setCallWaiting in CDMA : dial for set call waiting" + " prefix= " + cwPrefix); |
| |
| PhoneAccountHandle phoneAccountHandle = subscriptionIdToPhoneAccountHandle(getSubId()); |
| Bundle extras = new Bundle(); |
| extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle); |
| |
| final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class); |
| telecomManager.placeCall( |
| Uri.fromParts(PhoneAccount.SCHEME_TEL, cwPrefix, null), extras); |
| |
| AsyncResult.forMessage(onComplete, CommandsInterface.SS_STATUS_UNKNOWN, null); |
| onComplete.sendToTarget(); |
| } else { |
| loge("setCallWaiting: SS over CDMA not supported, can not complete"); |
| AsyncResult.forMessage(onComplete, CommandsInterface.SS_STATUS_UNKNOWN, null); |
| onComplete.sendToTarget(); |
| } |
| } |
| |
| @Override |
| public int getTerminalBasedCallWaitingState(boolean forCsOnly) { |
| return mCallWaitingController.getTerminalBasedCallWaitingState(forCsOnly); |
| } |
| |
| @Override |
| public void setTerminalBasedCallWaitingStatus(int state) { |
| if (mImsPhone != null) { |
| mImsPhone.setTerminalBasedCallWaitingStatus(state); |
| } |
| } |
| |
| @Override |
| public void setTerminalBasedCallWaitingSupported(boolean supported) { |
| mCallWaitingController.setTerminalBasedCallWaitingSupported(supported); |
| } |
| |
| @Override |
| public void getAvailableNetworks(Message response) { |
| if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) { |
| Message msg = obtainMessage(EVENT_GET_AVAILABLE_NETWORKS_DONE, response); |
| mCi.getAvailableNetworks(msg); |
| } else { |
| loge("getAvailableNetworks: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public void startNetworkScan(NetworkScanRequest nsr, Message response) { |
| mCi.startNetworkScan(nsr, response); |
| } |
| |
| @Override |
| public void stopNetworkScan(Message response) { |
| mCi.stopNetworkScan(response); |
| } |
| |
| @Override |
| public void setTTYMode(int ttyMode, Message onComplete) { |
| // Send out the TTY Mode change over RIL as well |
| super.setTTYMode(ttyMode, onComplete); |
| if (mImsPhone != null) { |
| mImsPhone.setTTYMode(ttyMode, onComplete); |
| } |
| } |
| |
| @Override |
| public void setUiTTYMode(int uiTtyMode, Message onComplete) { |
| if (mImsPhone != null) { |
| mImsPhone.setUiTTYMode(uiTtyMode, onComplete); |
| } |
| } |
| |
| @Override |
| public void setMute(boolean muted) { |
| mCT.setMute(muted); |
| } |
| |
| @Override |
| public boolean getMute() { |
| return mCT.getMute(); |
| } |
| |
| @Override |
| public void updateServiceLocation(WorkSource workSource) { |
| mSST.enableSingleLocationUpdate(workSource); |
| } |
| |
| @Override |
| public void enableLocationUpdates() { |
| mSST.enableLocationUpdates(); |
| } |
| |
| @Override |
| public void disableLocationUpdates() { |
| mSST.disableLocationUpdates(); |
| } |
| |
| @Override |
| public boolean getDataRoamingEnabled() { |
| return getDataSettingsManager().isDataRoamingEnabled(); |
| } |
| |
| @Override |
| public void setDataRoamingEnabled(boolean enable) { |
| getDataSettingsManager().setDataRoamingEnabled(enable); |
| } |
| |
| @Override |
| public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) { |
| mCi.registerForCdmaOtaProvision(h, what, obj); |
| } |
| |
| @Override |
| public void unregisterForCdmaOtaStatusChange(Handler h) { |
| mCi.unregisterForCdmaOtaProvision(h); |
| } |
| |
| @Override |
| public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) { |
| mSST.registerForSubscriptionInfoReady(h, what, obj); |
| } |
| |
| @Override |
| public void unregisterForSubscriptionInfoReady(Handler h) { |
| mSST.unregisterForSubscriptionInfoReady(h); |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @Override |
| public void setOnEcbModeExitResponse(Handler h, int what, Object obj) { |
| mEcmExitRespRegistrant = new Registrant(h, what, obj); |
| } |
| |
| @Override |
| public void unsetOnEcbModeExitResponse(Handler h) { |
| mEcmExitRespRegistrant.clear(); |
| } |
| |
| @Override |
| public void registerForCallWaiting(Handler h, int what, Object obj) { |
| mCT.registerForCallWaiting(h, what, obj); |
| } |
| |
| @Override |
| public void unregisterForCallWaiting(Handler h) { |
| mCT.unregisterForCallWaiting(h); |
| } |
| |
| /** |
| * Whether data is enabled by user. |
| */ |
| @Override |
| public boolean isUserDataEnabled() { |
| return getDataSettingsManager().isDataEnabledForReason( |
| TelephonyManager.DATA_ENABLED_REASON_USER); |
| } |
| |
| /** |
| * Removes the given MMI from the pending list and notifies |
| * registrants that it is complete. |
| * @param mmi MMI that is done |
| */ |
| public void onMMIDone(MmiCode mmi) { |
| /* Only notify complete if it's on the pending list. |
| * Otherwise, it's already been handled (eg, previously canceled). |
| * The exception is cancellation of an incoming USSD-REQUEST, which is |
| * not on the list. |
| */ |
| if (mPendingMMIs.remove(mmi) || (isPhoneTypeGsm() && (mmi.isUssdRequest() || |
| ((GsmMmiCode)mmi).isSsInfo()))) { |
| ResultReceiver receiverCallback = mmi.getUssdCallbackReceiver(); |
| if (receiverCallback != null) { |
| Rlog.i(LOG_TAG, "onMMIDone: invoking callback: " + mmi); |
| int returnCode = (mmi.getState() == MmiCode.State.COMPLETE) ? |
| TelephonyManager.USSD_RETURN_SUCCESS : TelephonyManager.USSD_RETURN_FAILURE; |
| sendUssdResponse(mmi.getDialString(), mmi.getMessage(), returnCode, |
| receiverCallback ); |
| } else { |
| Rlog.i(LOG_TAG, "onMMIDone: notifying registrants: " + mmi); |
| mMmiCompleteRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); |
| } |
| } else { |
| Rlog.i(LOG_TAG, "onMMIDone: invalid response or already handled; ignoring: " + mmi); |
| } |
| } |
| |
| public boolean supports3gppCallForwardingWhileRoaming() { |
| CarrierConfigManager configManager = (CarrierConfigManager) |
| getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle b = configManager.getConfigForSubId(getSubId()); |
| if (b != null) { |
| return b.getBoolean( |
| CarrierConfigManager.KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true); |
| } else { |
| // Default value set in CarrierConfigManager |
| return true; |
| } |
| } |
| |
| private void onNetworkInitiatedUssd(MmiCode mmi) { |
| Rlog.v(LOG_TAG, "onNetworkInitiatedUssd: mmi=" + mmi); |
| mMmiCompleteRegistrants.notifyRegistrants( |
| new AsyncResult(null, mmi, null)); |
| } |
| |
| /** ussdMode is one of CommandsInterface.USSD_MODE_* */ |
| private void onIncomingUSSD (int ussdMode, String ussdMessage) { |
| if (!isPhoneTypeGsm()) { |
| loge("onIncomingUSSD: not expected on GSM"); |
| } |
| |
| boolean isUssdError; |
| boolean isUssdRequest; |
| boolean isUssdRelease; |
| |
| isUssdRequest |
| = (ussdMode == CommandsInterface.USSD_MODE_REQUEST); |
| |
| isUssdError |
| = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY |
| && ussdMode != CommandsInterface.USSD_MODE_REQUEST); |
| |
| isUssdRelease = (ussdMode == CommandsInterface.USSD_MODE_NW_RELEASE); |
| |
| |
| // See comments in GsmMmiCode.java |
| // USSD requests aren't finished until one |
| // of these two events happen |
| GsmMmiCode found = null; |
| for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) { |
| if(((GsmMmiCode)mPendingMMIs.get(i)).isPendingUSSD()) { |
| found = (GsmMmiCode)mPendingMMIs.get(i); |
| break; |
| } |
| } |
| |
| if (found != null) { |
| // Complete pending USSD |
| if (isUssdRelease) { |
| found.onUssdRelease(); |
| } else if (isUssdError) { |
| found.onUssdFinishedError(); |
| } else { |
| found.onUssdFinished(ussdMessage, isUssdRequest); |
| } |
| } else if (!isUssdError && !TextUtils.isEmpty(ussdMessage)) { |
| // pending USSD not found |
| // The network may initiate its own USSD request |
| |
| // ignore everything that isnt a Notify or a Request |
| // also, discard if there is no message to present |
| GsmMmiCode mmi; |
| mmi = GsmMmiCode.newNetworkInitiatedUssd(ussdMessage, |
| isUssdRequest, |
| GsmCdmaPhone.this, |
| mUiccApplication.get()); |
| onNetworkInitiatedUssd(mmi); |
| } else if (isUssdError && !isUssdRelease) { |
| GsmMmiCode mmi; |
| mmi = GsmMmiCode.newNetworkInitiatedUssd(ussdMessage, |
| true, |
| GsmCdmaPhone.this, |
| mUiccApplication.get()); |
| mmi.onUssdFinishedError(); |
| } |
| } |
| |
| /** |
| * Make sure the network knows our preferred setting. |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private void syncClirSetting() { |
| if (!hasCalling()) return; |
| |
| SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); |
| migrateClirSettingIfNeeded(sp); |
| |
| int clirSetting = sp.getInt(CLIR_KEY + getSubId(), -1); |
| Rlog.i(LOG_TAG, "syncClirSetting: " + CLIR_KEY + getSubId() + "=" + clirSetting); |
| if (clirSetting >= 0) { |
| mCi.setCLIR(clirSetting, null); |
| } else { |
| // if there is no preference set, ensure the CLIR is updated to the default value in |
| // order to ensure that CLIR values in the RIL are not carried over during SIM swap. |
| mCi.setCLIR(CommandsInterface.CLIR_DEFAULT, null); |
| } |
| } |
| |
| /** |
| * Migrate CLIR setting with sudId mapping once if there's CLIR setting mapped with phoneId. |
| */ |
| private void migrateClirSettingIfNeeded(SharedPreferences sp) { |
| // Get old CLIR setting mapped with phoneId |
| int clirSetting = sp.getInt("clir_key" + getPhoneId(), -1); |
| if (clirSetting >= 0) { |
| // Migrate CLIR setting to new shared preference key with subId |
| Rlog.i(LOG_TAG, "Migrate CLIR setting: value=" + clirSetting + ", clir_key" |
| + getPhoneId() + " -> " + CLIR_KEY + getSubId()); |
| SharedPreferences.Editor editor = sp.edit(); |
| editor.putInt(CLIR_KEY + getSubId(), clirSetting); |
| |
| // Remove old CLIR setting key |
| editor.remove("clir_key" + getPhoneId()).commit(); |
| } |
| } |
| |
| private void handleRadioAvailable() { |
| mCi.getBasebandVersion(obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE)); |
| mCi.getImei(obtainMessage(EVENT_GET_DEVICE_IMEI_DONE)); |
| mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE)); |
| mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY)); |
| mCi.areUiccApplicationsEnabled(obtainMessage(EVENT_GET_UICC_APPS_ENABLEMENT_DONE)); |
| |
| handleNullCipherEnabledChange(); |
| handleIdentifierDisclosureNotificationPreferenceChange(); |
| handleNullCipherNotificationPreferenceChanged(); |
| } |
| |
| private void handleRadioOn() { |
| /* Proactively query voice radio technologies */ |
| mCi.getVoiceRadioTechnology(obtainMessage(EVENT_REQUEST_VOICE_RADIO_TECH_DONE)); |
| |
| if (!isPhoneTypeGsm()) { |
| mCdmaSubscriptionSource = mCdmaSSM.getCdmaSubscriptionSource(); |
| } |
| } |
| |
| private void handleRadioOffOrNotAvailable() { |
| if (isPhoneTypeGsm()) { |
| // Some MMI requests (eg USSD) are not completed |
| // within the course of a CommandsInterface request |
| // If the radio shuts off or resets while one of these |
| // is pending, we need to clean up. |
| |
| for (int i = mPendingMMIs.size() - 1; i >= 0; i--) { |
| if (((GsmMmiCode) mPendingMMIs.get(i)).isPendingUSSD()) { |
| ((GsmMmiCode) mPendingMMIs.get(i)).onUssdFinishedError(); |
| } |
| } |
| } |
| mRadioOffOrNotAvailableRegistrants.notifyRegistrants(); |
| } |
| |
| private void handleRadioPowerStateChange() { |
| @RadioPowerState int newState = mCi.getRadioState(); |
| Rlog.d(LOG_TAG, "handleRadioPowerStateChange, state= " + newState); |
| mNotifier.notifyRadioPowerStateChanged(this, newState); |
| TelephonyMetrics.getInstance().writeRadioState(mPhoneId, newState); |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| AsyncResult ar; |
| Message onComplete; |
| |
| switch (msg.what) { |
| case EVENT_RADIO_AVAILABLE: { |
| handleRadioAvailable(); |
| } |
| break; |
| case EVENT_GET_DEVICE_IMEI_DONE : |
| parseImeiInfo(msg); |
| break; |
| case EVENT_GET_DEVICE_IDENTITY_DONE:{ |
| ar = (AsyncResult)msg.obj; |
| |
| if (ar.exception != null) { |
| break; |
| } |
| String[] respId = (String[])ar.result; |
| if (TextUtils.isEmpty(mImei)) { |
| mImei = respId[0]; |
| mImeiSv = respId[1]; |
| } |
| mEsn = respId[2]; |
| mMeid = respId[3]; |
| // some modems return all 0's instead of null/empty string when MEID is unavailable |
| if (!TextUtils.isEmpty(mMeid) && mMeid.matches("^0*$")) { |
| logd("EVENT_GET_DEVICE_IDENTITY_DONE: set mMeid to null"); |
| mMeid = null; |
| } |
| } |
| break; |
| |
| case EVENT_EMERGENCY_CALLBACK_MODE_ENTER:{ |
| handleEnterEmergencyCallbackMode(msg); |
| } |
| break; |
| |
| case EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE:{ |
| handleExitEmergencyCallbackMode(msg); |
| } |
| break; |
| |
| case EVENT_MODEM_RESET: { |
| logd("Event EVENT_MODEM_RESET Received" + " isInEcm = " + isInEcm() |
| + " isPhoneTypeGsm = " + isPhoneTypeGsm() + " mImsPhone = " + mImsPhone); |
| if (isInEcm()) { |
| if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { |
| EmergencyStateTracker.getInstance().exitEmergencyCallbackMode(); |
| } else { |
| if (isPhoneTypeGsm()) { |
| if (mImsPhone != null) { |
| mImsPhone.handleExitEmergencyCallbackMode(); |
| } |
| } else { |
| handleExitEmergencyCallbackMode(msg); |
| } |
| } |
| } |
| } |
| break; |
| |
| case EVENT_RUIM_RECORDS_LOADED: |
| logd("Event EVENT_RUIM_RECORDS_LOADED Received"); |
| updateCurrentCarrierInProvider(); |
| break; |
| |
| case EVENT_RADIO_ON: |
| logd("Event EVENT_RADIO_ON Received"); |
| handleRadioOn(); |
| break; |
| |
| case EVENT_RIL_CONNECTED: |
| ar = (AsyncResult) msg.obj; |
| if (ar.exception == null && ar.result != null) { |
| mRilVersion = (Integer) ar.result; |
| } else { |
| logd("Unexpected exception on EVENT_RIL_CONNECTED"); |
| mRilVersion = -1; |
| } |
| break; |
| |
| case EVENT_VOICE_RADIO_TECH_CHANGED: |
| case EVENT_REQUEST_VOICE_RADIO_TECH_DONE: |
| String what = (msg.what == EVENT_VOICE_RADIO_TECH_CHANGED) ? |
| "EVENT_VOICE_RADIO_TECH_CHANGED" : "EVENT_REQUEST_VOICE_RADIO_TECH_DONE"; |
| ar = (AsyncResult) msg.obj; |
| if (ar.exception == null) { |
| if ((ar.result != null) && (((int[]) ar.result).length != 0)) { |
| int newVoiceTech = ((int[]) ar.result)[0]; |
| logd(what + ": newVoiceTech=" + newVoiceTech); |
| phoneObjectUpdater(newVoiceTech); |
| } else { |
| loge(what + ": has no tech!"); |
| } |
| } else { |
| loge(what + ": exception=" + ar.exception); |
| } |
| break; |
| |
| case EVENT_LINK_CAPACITY_CHANGED: |
| ar = (AsyncResult) msg.obj; |
| if (ar.exception == null && ar.result != null) { |
| updateLinkCapacityEstimate((List<LinkCapacityEstimate>) ar.result); |
| } else { |
| logd("Unexpected exception on EVENT_LINK_CAPACITY_CHANGED"); |
| } |
| break; |
| |
| case EVENT_UPDATE_PHONE_OBJECT: |
| phoneObjectUpdater(msg.arg1); |
| break; |
| |
| case EVENT_CARRIER_CONFIG_CHANGED: |
| // Only check for the voice radio tech if it not going to be updated by the voice |
| // registration changes. |
| if (!mContext.getResources().getBoolean( |
| com.android.internal.R.bool |
| .config_switch_phone_on_voice_reg_state_change)) { |
| mCi.getVoiceRadioTechnology(obtainMessage(EVENT_REQUEST_VOICE_RADIO_TECH_DONE)); |
| } |
| |
| CarrierConfigManager configMgr = (CarrierConfigManager) |
| getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| final PersistableBundle b = configMgr.getConfigForSubId(getSubId()); |
| if (b != null) { |
| updateBroadcastEmergencyCallStateChangesAfterCarrierConfigChanged(b); |
| updateCdmaRoamingSettingsAfterCarrierConfigChanged(b); |
| if (hasCalling()) { |
| updateNrSettingsAfterCarrierConfigChanged(b); |
| updateVoNrSettings(b); |
| } |
| updateSsOverCdmaSupported(b); |
| updateCarrierN1ModeSupported(b); |
| } else { |
| loge("Failed to retrieve a carrier config bundle for subId=" + getSubId()); |
| } |
| loadAllowedNetworksFromSubscriptionDatabase(); |
| // Obtain new radio capabilities from the modem, since some are SIM-dependent |
| mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY)); |
| break; |
| |
| case EVENT_SET_ROAMING_PREFERENCE_DONE: |
| logd("cdma_roaming_mode change is done"); |
| break; |
| |
| case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED: |
| logd("EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED"); |
| mCdmaSubscriptionSource = mCdmaSSM.getCdmaSubscriptionSource(); |
| break; |
| |
| case EVENT_REGISTERED_TO_NETWORK: |
| logd("Event EVENT_REGISTERED_TO_NETWORK Received"); |
| if (isPhoneTypeGsm()) { |
| syncClirSetting(); |
| } |
| break; |
| |
| case EVENT_SIM_RECORDS_LOADED: |
| updateCurrentCarrierInProvider(); |
| |
| // Check if this is a different SIM than the previous one. If so unset the |
| // voice mail number. |
| String imsi = getVmSimImsi(); |
| String imsiFromSIM = getSubscriberId(); |
| if ((!isPhoneTypeGsm() || imsi != null) && imsiFromSIM != null |
| && !imsiFromSIM.equals(imsi)) { |
| storeVoiceMailNumber(null); |
| setVmSimImsi(null); |
| } |
| |
| updateVoiceMail(); |
| |
| mSimRecordsLoadedRegistrants.notifyRegistrants(); |
| break; |
| |
| case EVENT_GET_BASEBAND_VERSION_DONE: |
| ar = (AsyncResult)msg.obj; |
| |
| if (ar.exception != null) { |
| break; |
| } |
| |
| if (DBG) logd("Baseband version: " + ar.result); |
| /* Android property value is limited to 91 characters, but low layer |
| could pass a larger version string. To avoid runtime exception, |
| truncate the string baseband version string to 45 characters at most |
| for this per sub property. Since the latter part of the version string |
| is meaningful, truncated the version string from the beginning and |
| keep the end of the version. |
| */ |
| String version = (String)ar.result; |
| if (version != null) { |
| int length = version.length(); |
| final int MAX_VERSION_LEN = SystemProperties.PROP_VALUE_MAX/2; |
| TelephonyManager.from(mContext).setBasebandVersionForPhone(getPhoneId(), |
| length <= MAX_VERSION_LEN ? version |
| : version.substring(length - MAX_VERSION_LEN, length)); |
| } |
| break; |
| |
| case EVENT_USSD: |
| ar = (AsyncResult)msg.obj; |
| |
| String[] ussdResult = (String[]) ar.result; |
| |
| if (ussdResult.length > 1) { |
| try { |
| onIncomingUSSD(Integer.parseInt(ussdResult[0]), ussdResult[1]); |
| } catch (NumberFormatException e) { |
| Rlog.w(LOG_TAG, "error parsing USSD"); |
| } |
| } |
| break; |
| |
| case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: { |
| logd("Event EVENT_RADIO_OFF_OR_NOT_AVAILABLE Received"); |
| handleRadioOffOrNotAvailable(); |
| break; |
| } |
| |
| case EVENT_RADIO_STATE_CHANGED: { |
| logd("EVENT EVENT_RADIO_STATE_CHANGED"); |
| handleRadioPowerStateChange(); |
| break; |
| } |
| |
| case EVENT_SSN: |
| logd("Event EVENT_SSN Received"); |
| if (isPhoneTypeGsm()) { |
| ar = (AsyncResult) msg.obj; |
| SuppServiceNotification not = (SuppServiceNotification) ar.result; |
| mSsnRegistrants.notifyRegistrants(ar); |
| } |
| break; |
| |
| case EVENT_REGISTRATION_FAILED: |
| logd("Event RegistrationFailed Received"); |
| ar = (AsyncResult) msg.obj; |
| RegistrationFailedEvent rfe = (RegistrationFailedEvent) ar.result; |
| mNotifier.notifyRegistrationFailed(this, rfe.cellIdentity, rfe.chosenPlmn, |
| rfe.domain, rfe.causeCode, rfe.additionalCauseCode); |
| break; |
| |
| case EVENT_BARRING_INFO_CHANGED: |
| logd("Event BarringInfoChanged Received"); |
| ar = (AsyncResult) msg.obj; |
| BarringInfo barringInfo = (BarringInfo) ar.result; |
| mNotifier.notifyBarringInfoChanged(this, barringInfo); |
| break; |
| |
| case EVENT_SET_CALL_FORWARD_DONE: |
| ar = (AsyncResult)msg.obj; |
| Cfu cfu = (Cfu) ar.userObj; |
| if (ar.exception == null) { |
| setVoiceCallForwardingFlag(1, msg.arg1 == 1, cfu.mSetCfNumber); |
| } |
| if (cfu.mOnComplete != null) { |
| AsyncResult.forMessage(cfu.mOnComplete, ar.result, ar.exception); |
| cfu.mOnComplete.sendToTarget(); |
| } |
| break; |
| |
| case EVENT_SET_VM_NUMBER_DONE: |
| ar = (AsyncResult)msg.obj; |
| if (((isPhoneTypeGsm() || mSimRecords != null) |
| && IccVmNotSupportedException.class.isInstance(ar.exception)) |
| || (!isPhoneTypeGsm() && mSimRecords == null |
| && IccException.class.isInstance(ar.exception))) { |
| storeVoiceMailNumber(mVmNumber); |
| ar.exception = null; |
| } |
| onComplete = (Message) ar.userObj; |
| if (onComplete != null) { |
| AsyncResult.forMessage(onComplete, ar.result, ar.exception); |
| onComplete.sendToTarget(); |
| } |
| break; |
| |
| |
| case EVENT_GET_CALL_FORWARD_DONE: |
| ar = (AsyncResult)msg.obj; |
| if (ar.exception == null) { |
| handleCfuQueryResult((CallForwardInfo[])ar.result); |
| } |
| onComplete = (Message) ar.userObj; |
| if (onComplete != null) { |
| AsyncResult.forMessage(onComplete, ar.result, ar.exception); |
| onComplete.sendToTarget(); |
| } |
| break; |
| |
| case EVENT_SET_NETWORK_AUTOMATIC: |
| // Automatic network selection from EF_CSP SIM record |
| ar = (AsyncResult) msg.obj; |
| if (mSST.mSS.getIsManualSelection()) { |
| setNetworkSelectionModeAutomatic((Message) ar.result); |
| logd("SET_NETWORK_SELECTION_AUTOMATIC: set to automatic"); |
| } else { |
| // prevent duplicate request which will push current PLMN to low priority |
| logd("SET_NETWORK_SELECTION_AUTOMATIC: already automatic, ignore"); |
| } |
| break; |
| |
| case EVENT_ICC_RECORD_EVENTS: |
| ar = (AsyncResult)msg.obj; |
| processIccRecordEvents((Integer)ar.result); |
| break; |
| |
| case EVENT_SET_CLIR_COMPLETE: |
| ar = (AsyncResult)msg.obj; |
| if (ar.exception == null) { |
| saveClirSetting(msg.arg1); |
| } |
| onComplete = (Message) ar.userObj; |
| if (onComplete != null) { |
| AsyncResult.forMessage(onComplete, ar.result, ar.exception); |
| onComplete.sendToTarget(); |
| } |
| break; |
| |
| case EVENT_SS: |
| ar = (AsyncResult)msg.obj; |
| logd("Event EVENT_SS received"); |
| if (isPhoneTypeGsm()) { |
| // SS data is already being handled through MMI codes. |
| // So, this result if processed as MMI response would help |
| // in re-using the existing functionality. |
| GsmMmiCode mmi = new GsmMmiCode(this, mUiccApplication.get()); |
| mmi.processSsData(ar); |
| } |
| break; |
| |
| case EVENT_GET_RADIO_CAPABILITY: |
| ar = (AsyncResult) msg.obj; |
| RadioCapability rc = (RadioCapability) ar.result; |
| if (ar.exception != null) { |
| Rlog.d(LOG_TAG, "get phone radio capability fail, no need to change " + |
| "mRadioCapability"); |
| } else { |
| radioCapabilityUpdated(rc, false); |
| } |
| Rlog.d(LOG_TAG, "EVENT_GET_RADIO_CAPABILITY: phone rc: " + rc); |
| break; |
| case EVENT_VRS_OR_RAT_CHANGED: |
| ar = (AsyncResult) msg.obj; |
| Pair<Integer, Integer> vrsRatPair = (Pair<Integer, Integer>) ar.result; |
| onVoiceRegStateOrRatChanged(vrsRatPair.first, vrsRatPair.second); |
| break; |
| |
| case EVENT_SET_CARRIER_DATA_ENABLED: |
| ar = (AsyncResult) msg.obj; |
| boolean enabled = (boolean) ar.result; |
| getDataSettingsManager().setDataEnabled( |
| TelephonyManager.DATA_ENABLED_REASON_CARRIER, enabled, |
| mContext.getOpPackageName()); |
| break; |
| case EVENT_GET_AVAILABLE_NETWORKS_DONE: |
| ar = (AsyncResult) msg.obj; |
| if (ar.exception == null && ar.result != null && mSST != null) { |
| List<OperatorInfo> operatorInfoList = (List<OperatorInfo>) ar.result; |
| List<OperatorInfo> filteredInfoList = new ArrayList<>(); |
| for (OperatorInfo operatorInfo : operatorInfoList) { |
| if (OperatorInfo.State.CURRENT == operatorInfo.getState()) { |
| filteredInfoList.add(new OperatorInfo( |
| mSST.filterOperatorNameByPattern( |
| operatorInfo.getOperatorAlphaLong()), |
| mSST.filterOperatorNameByPattern( |
| operatorInfo.getOperatorAlphaShort()), |
| operatorInfo.getOperatorNumeric(), |
| operatorInfo.getState() |
| )); |
| } else { |
| filteredInfoList.add(operatorInfo); |
| } |
| } |
| ar.result = filteredInfoList; |
| } |
| |
| onComplete = (Message) ar.userObj; |
| if (onComplete != null) { |
| AsyncResult.forMessage(onComplete, ar.result, ar.exception); |
| onComplete.sendToTarget(); |
| } |
| break; |
| case EVENT_GET_UICC_APPS_ENABLEMENT_DONE: |
| case EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED: |
| ar = (AsyncResult) msg.obj; |
| if (ar == null) return; |
| if (ar.exception != null) { |
| logd("Received exception on event" + msg.what + " : " + ar.exception); |
| return; |
| } |
| |
| mUiccApplicationsEnabled = (Boolean) ar.result; |
| // Intentional falling through. |
| case EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED: |
| reapplyUiccAppsEnablementIfNeeded(ENABLE_UICC_APPS_MAX_RETRIES); |
| break; |
| |
| case EVENT_REAPPLY_UICC_APPS_ENABLEMENT_DONE: { |
| ar = (AsyncResult) msg.obj; |
| if (ar == null || ar.exception == null) return; |
| Pair<Boolean, Integer> userObject = (Pair) ar.userObj; |
| if (userObject == null) return; |
| boolean expectedValue = userObject.first; |
| int retries = userObject.second; |
| CommandException.Error error = ((CommandException) ar.exception).getCommandError(); |
| loge("Error received when re-applying uicc application" |
| + " setting to " + expectedValue + " on phone " + mPhoneId |
| + " Error code: " + error + " retry count left: " + retries); |
| if (retries > 0 && (error == GENERIC_FAILURE || error == SIM_BUSY)) { |
| // Retry for certain errors, but not for others like RADIO_NOT_AVAILABLE or |
| // SIM_ABSENT, as they will trigger it whey they become available. |
| postDelayed(()->reapplyUiccAppsEnablementIfNeeded(retries - 1), |
| REAPPLY_UICC_APPS_SETTING_RETRY_TIME_GAP_IN_MS); |
| } |
| break; |
| } |
| case EVENT_RESET_CARRIER_KEY_IMSI_ENCRYPTION: { |
| resetCarrierKeysForImsiEncryption(); |
| break; |
| } |
| case EVENT_SET_VONR_ENABLED_DONE: |
| logd("EVENT_SET_VONR_ENABLED_DONE is done"); |
| break; |
| case EVENT_SUBSCRIPTIONS_CHANGED: |
| logd("EVENT_SUBSCRIPTIONS_CHANGED"); |
| updateUsageSetting(); |
| updateNullCipherNotifier(); |
| break; |
| case EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE: |
| logd("EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE"); |
| ar = (AsyncResult) msg.obj; |
| mIsNullCipherAndIntegritySupported = doesResultIndicateModemSupport(ar); |
| break; |
| |
| case EVENT_IMS_DEREGISTRATION_TRIGGERED: |
| logd("EVENT_IMS_DEREGISTRATION_TRIGGERED"); |
| ar = (AsyncResult) msg.obj; |
| if (ar.exception == null) { |
| mImsPhone.triggerImsDeregistration(((int[]) ar.result)[0]); |
| } else { |
| Rlog.e(LOG_TAG, "Unexpected unsol with exception", ar.exception); |
| } |
| break; |
| |
| case EVENT_TRIGGER_NOTIFY_ANBR: |
| logd("EVENT_TRIGGER_NOTIFY_ANBR"); |
| ar = (AsyncResult) msg.obj; |
| if (ar.exception == null) { |
| if (mImsPhone != null) { |
| mImsPhone.triggerNotifyAnbr(((int[]) ar.result)[0], ((int[]) ar.result)[1], |
| ((int[]) ar.result)[2]); |
| } |
| } |
| break; |
| |
| case EVENT_GET_N1_MODE_ENABLED_DONE: |
| logd("EVENT_GET_N1_MODE_ENABLED_DONE"); |
| ar = (AsyncResult) msg.obj; |
| if (ar == null || ar.exception != null |
| || ar.result == null || !(ar.result instanceof Boolean)) { |
| Rlog.e(LOG_TAG, "Failed to Retrieve N1 Mode", ar.exception); |
| if (ar != null && ar.userObj instanceof Message) { |
| // original requester's message is stashed in the userObj |
| final Message rsp = (Message) ar.userObj; |
| AsyncResult.forMessage(rsp, null, ar.exception); |
| rsp.sendToTarget(); |
| } |
| break; |
| } |
| |
| mModemN1Mode = (Boolean) ar.result; |
| maybeUpdateModemN1Mode((Message) ar.userObj); |
| break; |
| |
| case EVENT_SET_N1_MODE_ENABLED_DONE: |
| logd("EVENT_SET_N1_MODE_ENABLED_DONE"); |
| ar = (AsyncResult) msg.obj; |
| if (ar == null || ar.exception != null) { |
| Rlog.e(LOG_TAG, "Failed to Set N1 Mode", ar.exception); |
| // Set failed, so we have no idea at this point. |
| mModemN1Mode = null; |
| } |
| if (ar != null && ar.userObj instanceof Message) { |
| // original requester's message is stashed in the userObj |
| final Message rsp = (Message) ar.userObj; |
| AsyncResult.forMessage(rsp, null, ar.exception); |
| rsp.sendToTarget(); |
| } |
| break; |
| |
| case EVENT_IMEI_MAPPING_CHANGED: |
| logd("EVENT_GET_DEVICE_IMEI_CHANGE_DONE phoneId = " + getPhoneId()); |
| parseImeiInfo(msg); |
| break; |
| |
| case EVENT_CELL_IDENTIFIER_DISCLOSURE: |
| logd("EVENT_CELL_IDENTIFIER_DISCLOSURE phoneId = " + getPhoneId()); |
| |
| ar = (AsyncResult) msg.obj; |
| if (ar == null || ar.result == null || ar.exception != null) { |
| Rlog.e( |
| LOG_TAG, |
| "Failed to process cellular identifier disclosure", |
| ar.exception); |
| break; |
| } |
| |
| CellularIdentifierDisclosure disclosure = (CellularIdentifierDisclosure) ar.result; |
| if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents() |
| && mIdentifierDisclosureNotifier != null |
| && disclosure != null) { |
| mIdentifierDisclosureNotifier.addDisclosure(mContext, getSubId(), disclosure); |
| } |
| break; |
| |
| case EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE: |
| logd("EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE"); |
| ar = (AsyncResult) msg.obj; |
| mIsIdentifierDisclosureTransparencySupported = doesResultIndicateModemSupport(ar); |
| break; |
| |
| case EVENT_SECURITY_ALGORITHM_UPDATE: |
| logd("EVENT_SECURITY_ALGORITHM_UPDATE phoneId = " + getPhoneId()); |
| if (mFeatureFlags.enableModemCipherTransparencyUnsolEvents() |
| && mNullCipherNotifier != null) { |
| ar = (AsyncResult) msg.obj; |
| SecurityAlgorithmUpdate update = (SecurityAlgorithmUpdate) ar.result; |
| mNullCipherNotifier.onSecurityAlgorithmUpdate(mContext, getPhoneId(), |
| getSubId(), update); |
| } |
| break; |
| |
| case EVENT_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED_DONE: |
| logd("EVENT_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED_DONE"); |
| ar = (AsyncResult) msg.obj; |
| mIsNullCipherNotificationSupported = doesResultIndicateModemSupport(ar); |
| break; |
| |
| default: |
| super.handleMessage(msg); |
| } |
| } |
| |
| private boolean doesResultIndicateModemSupport(AsyncResult ar) { |
| // We can only say that the modem supports a call without ambiguity if there |
| // is no exception set on the response. Testing for REQUEST_NOT_SUPPORTED, is |
| // insufficient because the modem or the RIL could still return exceptions for temporary |
| // failures even when the feature is unsupported. |
| return (ar == null || ar.exception == null); |
| } |
| |
| private void parseImeiInfo(Message msg) { |
| AsyncResult ar = (AsyncResult)msg.obj; |
| if (ar.exception != null || ar.result == null) { |
| loge("parseImeiInfo :: Exception received : " + ar.exception); |
| return; |
| } |
| ImeiInfo imeiInfo = (ImeiInfo) ar.result; |
| if (!TextUtils.isEmpty(imeiInfo.imei)) { |
| mImeiType = imeiInfo.type; |
| mImei = imeiInfo.imei; |
| mImeiSv = imeiInfo.svn; |
| } else { |
| loge("parseImeiInfo :: IMEI value is empty"); |
| } |
| } |
| |
| /** |
| * Check if a different SIM is inserted at this slot from the last time. Storing last subId |
| * in SharedPreference for now to detect SIM change. |
| * |
| * @return {@code true} if current slot mapping changed; {@code false} otherwise. |
| */ |
| private boolean currentSlotSubIdChanged() { |
| SharedPreferences sp = |
| PreferenceManager.getDefaultSharedPreferences(mContext); |
| int storedSubId = sp.getInt(CURR_SUBID + mPhoneId, -1); |
| boolean changed = storedSubId != getSubId(); |
| if (changed) { |
| // Update stored subId |
| SharedPreferences.Editor editor = sp.edit(); |
| editor.putInt(CURR_SUBID + mPhoneId, getSubId()); |
| editor.apply(); |
| } |
| Rlog.d(LOG_TAG, "currentSlotSubIdChanged: changed=" + changed); |
| return changed; |
| } |
| |
| public UiccCardApplication getUiccCardApplication() { |
| if (isPhoneTypeGsm()) { |
| return mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP); |
| } else { |
| return mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP2); |
| } |
| } |
| |
| // todo: check if ICC availability needs to be handled here. mSimRecords should not be needed |
| // now because APIs can be called directly on UiccProfile, and that should handle the requests |
| // correctly based on supported apps, voice RAT, etc. |
| @Override |
| protected void onUpdateIccAvailability() { |
| if (mUiccController == null ) { |
| return; |
| } |
| |
| UiccCardApplication newUiccApplication = null; |
| |
| // Update mIsimUiccRecords |
| if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) { |
| newUiccApplication = |
| mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_IMS); |
| IsimUiccRecords newIsimUiccRecords = null; |
| |
| if (newUiccApplication != null) { |
| newIsimUiccRecords = (IsimUiccRecords) newUiccApplication.getIccRecords(); |
| if (DBG) logd("New ISIM application found"); |
| } |
| mIsimUiccRecords = newIsimUiccRecords; |
| } |
| |
| // Update mSimRecords |
| if (mSimRecords != null) { |
| mSimRecords.unregisterForRecordsLoaded(this); |
| } |
| if (isPhoneTypeCdmaLte() || isPhoneTypeCdma()) { |
| newUiccApplication = mUiccController.getUiccCardApplication(mPhoneId, |
| UiccController.APP_FAM_3GPP); |
| SIMRecords newSimRecords = null; |
| if (newUiccApplication != null) { |
| newSimRecords = (SIMRecords) newUiccApplication.getIccRecords(); |
| } |
| mSimRecords = newSimRecords; |
| if (mSimRecords != null) { |
| mSimRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); |
| } |
| } else { |
| mSimRecords = null; |
| } |
| |
| // Update mIccRecords, mUiccApplication, mIccPhoneBookIntManager |
| newUiccApplication = getUiccCardApplication(); |
| if (!isPhoneTypeGsm() && newUiccApplication == null) { |
| logd("can't find 3GPP2 application; trying APP_FAM_3GPP"); |
| newUiccApplication = mUiccController.getUiccCardApplication(mPhoneId, |
| UiccController.APP_FAM_3GPP); |
| } |
| |
| UiccCardApplication app = mUiccApplication.get(); |
| if (app != newUiccApplication) { |
| if (app != null) { |
| if (DBG) logd("Removing stale icc objects."); |
| if (mIccRecords.get() != null) { |
| unregisterForIccRecordEvents(); |
| mIccPhoneBookIntManager.updateIccRecords(null); |
| } |
| mIccRecords.set(null); |
| mUiccApplication.set(null); |
| } |
| if (newUiccApplication != null) { |
| if (DBG) { |
| logd("New Uicc application found. type = " + newUiccApplication.getType()); |
| } |
| final IccRecords iccRecords = newUiccApplication.getIccRecords(); |
| mUiccApplication.set(newUiccApplication); |
| mIccRecords.set(iccRecords); |
| registerForIccRecordEvents(); |
| mIccPhoneBookIntManager.updateIccRecords(iccRecords); |
| if (iccRecords != null) { |
| final String simOperatorNumeric = iccRecords.getOperatorNumeric(); |
| if (DBG) { |
| logd("New simOperatorNumeric = " + simOperatorNumeric); |
| } |
| if (!TextUtils.isEmpty(simOperatorNumeric)) { |
| TelephonyManager.from(mContext).setSimOperatorNumericForPhone(mPhoneId, |
| simOperatorNumeric); |
| } |
| } |
| updateCurrentCarrierInProvider(); |
| } |
| } |
| |
| reapplyUiccAppsEnablementIfNeeded(ENABLE_UICC_APPS_MAX_RETRIES); |
| } |
| |
| private void processIccRecordEvents(int eventCode) { |
| switch (eventCode) { |
| case IccRecords.EVENT_CFI: |
| logi("processIccRecordEvents: EVENT_CFI"); |
| notifyCallForwardingIndicator(); |
| break; |
| } |
| } |
| |
| /** |
| * Sets the "current" field in the telephony provider according to the SIM's operator |
| * |
| * @return true for success; false otherwise. |
| */ |
| @Override |
| public boolean updateCurrentCarrierInProvider() { |
| long currentDds = SubscriptionManager.getDefaultDataSubscriptionId(); |
| String operatorNumeric = getOperatorNumeric(); |
| |
| logd("updateCurrentCarrierInProvider: mSubId = " + getSubId() |
| + " currentDds = " + currentDds + " operatorNumeric = " + operatorNumeric); |
| |
| if (!TextUtils.isEmpty(operatorNumeric) && (getSubId() == currentDds)) { |
| try { |
| Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"); |
| ContentValues map = new ContentValues(); |
| map.put(Telephony.Carriers.NUMERIC, operatorNumeric); |
| mContext.getContentResolver().insert(uri, map); |
| return true; |
| } catch (SQLException e) { |
| Rlog.e(LOG_TAG, "Can't store current operator", e); |
| } |
| } |
| return false; |
| } |
| |
| //CDMA |
| /** |
| * Sets the "current" field in the telephony provider according to the |
| * build-time operator numeric property |
| * |
| * @return true for success; false otherwise. |
| */ |
| private boolean updateCurrentCarrierInProvider(String operatorNumeric) { |
| if (isPhoneTypeCdma() |
| || (isPhoneTypeCdmaLte() && mUiccController.getUiccCardApplication(mPhoneId, |
| UiccController.APP_FAM_3GPP) == null)) { |
| logd("CDMAPhone: updateCurrentCarrierInProvider called"); |
| if (!TextUtils.isEmpty(operatorNumeric)) { |
| try { |
| Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"); |
| ContentValues map = new ContentValues(); |
| map.put(Telephony.Carriers.NUMERIC, operatorNumeric); |
| logd("updateCurrentCarrierInProvider from system: numeric=" + operatorNumeric); |
| getContext().getContentResolver().insert(uri, map); |
| |
| // Updates MCC MNC device configuration information |
| logd("update mccmnc=" + operatorNumeric); |
| MccTable.updateMccMncConfiguration(mContext, operatorNumeric); |
| |
| return true; |
| } catch (SQLException e) { |
| Rlog.e(LOG_TAG, "Can't store current operator", e); |
| } |
| } |
| return false; |
| } else { // isPhoneTypeCdmaLte() |
| if (DBG) logd("updateCurrentCarrierInProvider not updated X retVal=" + true); |
| return true; |
| } |
| } |
| |
| private void handleCfuQueryResult(CallForwardInfo[] infos) { |
| if (infos == null || infos.length == 0) { |
| // Assume the default is not active |
| // Set unconditional CFF in SIM to false |
| setVoiceCallForwardingFlag(1, false, null); |
| } else { |
| for (int i = 0, s = infos.length; i < s; i++) { |
| if ((infos[i].serviceClass & SERVICE_CLASS_VOICE) != 0) { |
| setVoiceCallForwardingFlag(1, (infos[i].status == 1), |
| infos[i].number); |
| // should only have the one |
| break; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Retrieves the IccPhoneBookInterfaceManager of the GsmCdmaPhone |
| */ |
| @Override |
| public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){ |
| return mIccPhoneBookIntManager; |
| } |
| |
| /** |
| * Activate or deactivate cell broadcast SMS. |
| * |
| * @param activate 0 = activate, 1 = deactivate |
| * @param response Callback message is empty on completion |
| */ |
| @Override |
| public void activateCellBroadcastSms(int activate, Message response) { |
| loge("[GsmCdmaPhone] activateCellBroadcastSms() is obsolete; use SmsManager"); |
| response.sendToTarget(); |
| } |
| |
| /** |
| * Query the current configuration of cdma cell broadcast SMS. |
| * |
| * @param response Callback message is empty on completion |
| */ |
| @Override |
| public void getCellBroadcastSmsConfig(Message response) { |
| loge("[GsmCdmaPhone] getCellBroadcastSmsConfig() is obsolete; use SmsManager"); |
| response.sendToTarget(); |
| } |
| |
| /** |
| * Configure cdma cell broadcast SMS. |
| * |
| * @param response Callback message is empty on completion |
| */ |
| @Override |
| public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) { |
| loge("[GsmCdmaPhone] setCellBroadcastSmsConfig() is obsolete; use SmsManager"); |
| response.sendToTarget(); |
| } |
| |
| /** |
| * Returns true if OTA Service Provisioning needs to be performed. |
| */ |
| @Override |
| public boolean needsOtaServiceProvisioning() { |
| if (isPhoneTypeGsm()) { |
| return false; |
| } else { |
| return mSST.getOtasp() != TelephonyManager.OTASP_NOT_NEEDED; |
| } |
| } |
| |
| @Override |
| public boolean isCspPlmnEnabled() { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.isCspPlmnEnabled() : false; |
| } |
| |
| /** |
| * Whether manual select is now allowed and we should set |
| * to auto network select mode. |
| */ |
| public boolean shouldForceAutoNetworkSelect() { |
| |
| int networkTypeBitmask = RadioAccessFamily.getRafFromNetworkType( |
| RILConstants.PREFERRED_NETWORK_MODE); |
| int subId = getSubId(); |
| |
| // If it's invalid subId, we shouldn't force to auto network select mode. |
| if (!SubscriptionManager.isValidSubscriptionId(subId)) { |
| return false; |
| } |
| |
| networkTypeBitmask = (int) getAllowedNetworkTypes( |
| TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER); |
| |
| logd("shouldForceAutoNetworkSelect in mode = " + networkTypeBitmask); |
| /* |
| * For multimode targets in global mode manual network |
| * selection is disallowed. So we should force auto select mode. |
| */ |
| if (isManualSelProhibitedInGlobalMode() |
| && ((networkTypeBitmask == RadioAccessFamily.getRafFromNetworkType( |
| TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)) |
| || (networkTypeBitmask == RadioAccessFamily.getRafFromNetworkType( |
| TelephonyManager.NETWORK_MODE_GLOBAL)))) { |
| logd("Should force auto network select mode = " + networkTypeBitmask); |
| return true; |
| } else { |
| logd("Should not force auto network select mode = " + networkTypeBitmask); |
| } |
| |
| /* |
| * Single mode phone with - GSM network modes/global mode |
| * LTE only for 3GPP |
| * LTE centric + 3GPP Legacy |
| * Note: the actual enabling/disabling manual selection for these |
| * cases will be controlled by csp |
| */ |
| return false; |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private boolean isManualSelProhibitedInGlobalMode() { |
| boolean isProhibited = false; |
| final String configString = getContext().getResources().getString(com.android.internal |
| .R.string.prohibit_manual_network_selection_in_gobal_mode); |
| |
| if (!TextUtils.isEmpty(configString)) { |
| String[] configArray = configString.split(";"); |
| |
| if (configArray != null && |
| ((configArray.length == 1 && configArray[0].equalsIgnoreCase("true")) || |
| (configArray.length == 2 && !TextUtils.isEmpty(configArray[1]) && |
| configArray[0].equalsIgnoreCase("true") && |
| isMatchGid(configArray[1])))) { |
| isProhibited = true; |
| } |
| } |
| logd("isManualNetSelAllowedInGlobal in current carrier is " + isProhibited); |
| return isProhibited; |
| } |
| |
| private void registerForIccRecordEvents() { |
| IccRecords r = mIccRecords.get(); |
| if (r == null) { |
| return; |
| } |
| if (isPhoneTypeGsm()) { |
| r.registerForNetworkSelectionModeAutomatic( |
| this, EVENT_SET_NETWORK_AUTOMATIC, null); |
| r.registerForRecordsEvents(this, EVENT_ICC_RECORD_EVENTS, null); |
| r.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); |
| } else { |
| r.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null); |
| if (isPhoneTypeCdmaLte()) { |
| // notify simRecordsLoaded registrants for cdmaLte phone |
| r.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); |
| } |
| } |
| } |
| |
| private void unregisterForIccRecordEvents() { |
| IccRecords r = mIccRecords.get(); |
| if (r == null) { |
| return; |
| } |
| r.unregisterForNetworkSelectionModeAutomatic(this); |
| r.unregisterForRecordsEvents(this); |
| r.unregisterForRecordsLoaded(this); |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @Override |
| public void exitEmergencyCallbackMode() { |
| if (DBG) { |
| Rlog.d(LOG_TAG, "exitEmergencyCallbackMode: mImsPhone=" + mImsPhone |
| + " isPhoneTypeGsm=" + isPhoneTypeGsm()); |
| } |
| if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { |
| EmergencyStateTracker.getInstance().exitEmergencyCallbackMode(); |
| return; |
| } |
| if (mImsPhone != null && mImsPhone.isInImsEcm()) { |
| mImsPhone.exitEmergencyCallbackMode(); |
| } else { |
| if (mWakeLock.isHeld()) { |
| mWakeLock.release(); |
| } |
| Message msg = null; |
| if (mIsTestingEmergencyCallbackMode) { |
| // prevent duplicate exit messages from happening due to this message being handled |
| // as well as an UNSOL when the modem exits ECbM. Instead, only register for this |
| // message callback when this is a test and we will not be receiving the UNSOL from |
| // the modem. |
| msg = obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE); |
| } |
| mCi.exitEmergencyCallbackMode(msg); |
| } |
| } |
| |
| //CDMA |
| private void handleEnterEmergencyCallbackMode(Message msg) { |
| if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { |
| Rlog.d(LOG_TAG, "DomainSelection enabled: ignore ECBM enter event."); |
| return; |
| } |
| if (DBG) { |
| Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode, isInEcm()=" |
| + isInEcm()); |
| } |
| // if phone is not in Ecm mode, and it's changed to Ecm mode |
| if (!isInEcm()) { |
| setIsInEcm(true); |
| |
| // notify change |
| sendEmergencyCallbackModeChange(); |
| |
| // Post this runnable so we will automatically exit |
| // if no one invokes exitEmergencyCallbackMode() directly. |
| long delayInMillis = TelephonyProperties.ecm_exit_timer() |
| .orElse(DEFAULT_ECM_EXIT_TIMER_VALUE); |
| postDelayed(mExitEcmRunnable, delayInMillis); |
| // We don't want to go to sleep while in Ecm |
| mWakeLock.acquire(); |
| } |
| } |
| |
| //CDMA |
| private void handleExitEmergencyCallbackMode(Message msg) { |
| if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { |
| Rlog.d(LOG_TAG, "DomainSelection enabled: ignore ECBM exit event."); |
| return; |
| } |
| AsyncResult ar = (AsyncResult)msg.obj; |
| if (DBG) { |
| Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode,ar.exception , isInEcm=" |
| + ar.exception + isInEcm()); |
| } |
| // Remove pending exit Ecm runnable, if any |
| removeCallbacks(mExitEcmRunnable); |
| |
| if (mEcmExitRespRegistrant != null) { |
| mEcmExitRespRegistrant.notifyRegistrant(ar); |
| } |
| // if exiting is successful or we are testing and the modem responded with an error upon |
| // exit, which may occur in some IRadio implementations. |
| if (ar.exception == null || mIsTestingEmergencyCallbackMode) { |
| if (isInEcm()) { |
| setIsInEcm(false); |
| } |
| |
| // release wakeLock |
| if (mWakeLock.isHeld()) { |
| mWakeLock.release(); |
| } |
| |
| // send an Intent |
| sendEmergencyCallbackModeChange(); |
| notifyEmergencyCallRegistrants(false); |
| } |
| mIsTestingEmergencyCallbackMode = false; |
| } |
| |
| //CDMA |
| public void notifyEmergencyCallRegistrants(boolean started) { |
| mEmergencyCallToggledRegistrants.notifyResult(started ? 1 : 0); |
| } |
| |
| //CDMA |
| /** |
| * Handle to cancel or restart Ecm timer in emergency call back mode |
| * if action is CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled; |
| * otherwise, restart Ecm timer and notify apps the timer is restarted. |
| */ |
| public void handleTimerInEmergencyCallbackMode(int action) { |
| if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) return; |
| switch(action) { |
| case CANCEL_ECM_TIMER: |
| removeCallbacks(mExitEcmRunnable); |
| mEcmTimerResetRegistrants.notifyResult(Boolean.TRUE); |
| setEcmCanceledForEmergency(true /*isCanceled*/); |
| break; |
| case RESTART_ECM_TIMER: |
| long delayInMillis = TelephonyProperties.ecm_exit_timer() |
| .orElse(DEFAULT_ECM_EXIT_TIMER_VALUE); |
| postDelayed(mExitEcmRunnable, delayInMillis); |
| mEcmTimerResetRegistrants.notifyResult(Boolean.FALSE); |
| setEcmCanceledForEmergency(false /*isCanceled*/); |
| break; |
| default: |
| Rlog.e(LOG_TAG, "handleTimerInEmergencyCallbackMode, unsupported action " + action); |
| } |
| } |
| |
| //CDMA |
| private static final String IS683A_FEATURE_CODE = "*228"; |
| private static final int IS683A_FEATURE_CODE_NUM_DIGITS = 4; |
| private static final int IS683A_SYS_SEL_CODE_NUM_DIGITS = 2; |
| private static final int IS683A_SYS_SEL_CODE_OFFSET = 4; |
| |
| private static final int IS683_CONST_800MHZ_A_BAND = 0; |
| private static final int IS683_CONST_800MHZ_B_BAND = 1; |
| private static final int IS683_CONST_1900MHZ_A_BLOCK = 2; |
| private static final int IS683_CONST_1900MHZ_B_BLOCK = 3; |
| private static final int IS683_CONST_1900MHZ_C_BLOCK = 4; |
| private static final int IS683_CONST_1900MHZ_D_BLOCK = 5; |
| private static final int IS683_CONST_1900MHZ_E_BLOCK = 6; |
| private static final int IS683_CONST_1900MHZ_F_BLOCK = 7; |
| private static final int INVALID_SYSTEM_SELECTION_CODE = -1; |
| |
| // Define the pattern/format for carrier specified OTASP number schema. |
| // It separates by comma and/or whitespace. |
| private static Pattern pOtaSpNumSchema = Pattern.compile("[,\\s]+"); |
| |
| //CDMA |
| private static boolean isIs683OtaSpDialStr(String dialStr) { |
| int sysSelCodeInt; |
| boolean isOtaspDialString = false; |
| int dialStrLen = dialStr.length(); |
| |
| if (dialStrLen == IS683A_FEATURE_CODE_NUM_DIGITS) { |
| if (dialStr.equals(IS683A_FEATURE_CODE)) { |
| isOtaspDialString = true; |
| } |
| } else { |
| sysSelCodeInt = extractSelCodeFromOtaSpNum(dialStr); |
| switch (sysSelCodeInt) { |
| case IS683_CONST_800MHZ_A_BAND: |
| case IS683_CONST_800MHZ_B_BAND: |
| case IS683_CONST_1900MHZ_A_BLOCK: |
| case IS683_CONST_1900MHZ_B_BLOCK: |
| case IS683_CONST_1900MHZ_C_BLOCK: |
| case IS683_CONST_1900MHZ_D_BLOCK: |
| case IS683_CONST_1900MHZ_E_BLOCK: |
| case IS683_CONST_1900MHZ_F_BLOCK: |
| isOtaspDialString = true; |
| break; |
| default: |
| break; |
| } |
| } |
| return isOtaspDialString; |
| } |
| |
| //CDMA |
| /** |
| * This function extracts the system selection code from the dial string. |
| */ |
| private static int extractSelCodeFromOtaSpNum(String dialStr) { |
| int dialStrLen = dialStr.length(); |
| int sysSelCodeInt = INVALID_SYSTEM_SELECTION_CODE; |
| |
| if ((dialStr.regionMatches(0, IS683A_FEATURE_CODE, |
| 0, IS683A_FEATURE_CODE_NUM_DIGITS)) && |
| (dialStrLen >= (IS683A_FEATURE_CODE_NUM_DIGITS + |
| IS683A_SYS_SEL_CODE_NUM_DIGITS))) { |
| // Since we checked the condition above, the system selection code |
| // extracted from dialStr will not cause any exception |
| sysSelCodeInt = Integer.parseInt ( |
| dialStr.substring (IS683A_FEATURE_CODE_NUM_DIGITS, |
| IS683A_FEATURE_CODE_NUM_DIGITS + IS683A_SYS_SEL_CODE_NUM_DIGITS)); |
| } |
| if (DBG) Rlog.d(LOG_TAG, "extractSelCodeFromOtaSpNum " + sysSelCodeInt); |
| return sysSelCodeInt; |
| } |
| |
| //CDMA |
| /** |
| * This function checks if the system selection code extracted from |
| * the dial string "sysSelCodeInt' is the system selection code specified |
| * in the carrier ota sp number schema "sch". |
| */ |
| private static boolean checkOtaSpNumBasedOnSysSelCode(int sysSelCodeInt, String sch[]) { |
| boolean isOtaSpNum = false; |
| try { |
| // Get how many number of system selection code ranges |
| int selRc = Integer.parseInt(sch[1]); |
| for (int i = 0; i < selRc; i++) { |
| if (!TextUtils.isEmpty(sch[i*2+2]) && !TextUtils.isEmpty(sch[i*2+3])) { |
| int selMin = Integer.parseInt(sch[i*2+2]); |
| int selMax = Integer.parseInt(sch[i*2+3]); |
| // Check if the selection code extracted from the dial string falls |
| // within any of the range pairs specified in the schema. |
| if ((sysSelCodeInt >= selMin) && (sysSelCodeInt <= selMax)) { |
| isOtaSpNum = true; |
| break; |
| } |
| } |
| } |
| } catch (NumberFormatException ex) { |
| // If the carrier ota sp number schema is not correct, we still allow dial |
| // and only log the error: |
| Rlog.e(LOG_TAG, "checkOtaSpNumBasedOnSysSelCode, error", ex); |
| } |
| return isOtaSpNum; |
| } |
| |
| //CDMA |
| /** |
| * The following function checks if a dial string is a carrier specified |
| * OTASP number or not by checking against the OTASP number schema stored |
| * in PROPERTY_OTASP_NUM_SCHEMA. |
| * |
| * Currently, there are 2 schemas for carriers to specify the OTASP number: |
| * 1) Use system selection code: |
| * The schema is: |
| * SELC,the # of code pairs,min1,max1,min2,max2,... |
| * e.g "SELC,3,10,20,30,40,60,70" indicates that there are 3 pairs of |
| * selection codes, and they are {10,20}, {30,40} and {60,70} respectively. |
| * |
| * 2) Use feature code: |
| * The schema is: |
| * "FC,length of feature code,feature code". |
| * e.g "FC,2,*2" indicates that the length of the feature code is 2, |
| * and the code itself is "*2". |
| */ |
| private boolean isCarrierOtaSpNum(String dialStr) { |
| boolean isOtaSpNum = false; |
| int sysSelCodeInt = extractSelCodeFromOtaSpNum(dialStr); |
| if (sysSelCodeInt == INVALID_SYSTEM_SELECTION_CODE) { |
| return isOtaSpNum; |
| } |
| // mCarrierOtaSpNumSchema is retrieved from PROPERTY_OTASP_NUM_SCHEMA: |
| if (!TextUtils.isEmpty(mCarrierOtaSpNumSchema)) { |
| Matcher m = pOtaSpNumSchema.matcher(mCarrierOtaSpNumSchema); |
| if (DBG) { |
| Rlog.d(LOG_TAG, "isCarrierOtaSpNum,schema" + mCarrierOtaSpNumSchema); |
| } |
| |
| if (m.find()) { |
| String sch[] = pOtaSpNumSchema.split(mCarrierOtaSpNumSchema); |
| // If carrier uses system selection code mechanism |
| if (!TextUtils.isEmpty(sch[0]) && sch[0].equals("SELC")) { |
| if (sysSelCodeInt!=INVALID_SYSTEM_SELECTION_CODE) { |
| isOtaSpNum=checkOtaSpNumBasedOnSysSelCode(sysSelCodeInt,sch); |
| } else { |
| if (DBG) { |
| Rlog.d(LOG_TAG, "isCarrierOtaSpNum,sysSelCodeInt is invalid"); |
| } |
| } |
| } else if (!TextUtils.isEmpty(sch[0]) && sch[0].equals("FC")) { |
| int fcLen = Integer.parseInt(sch[1]); |
| String fc = sch[2]; |
| if (dialStr.regionMatches(0,fc,0,fcLen)) { |
| isOtaSpNum = true; |
| } else { |
| if (DBG) Rlog.d(LOG_TAG, "isCarrierOtaSpNum,not otasp number"); |
| } |
| } else { |
| if (DBG) { |
| Rlog.d(LOG_TAG, "isCarrierOtaSpNum,ota schema not supported" + sch[0]); |
| } |
| } |
| } else { |
| if (DBG) { |
| Rlog.d(LOG_TAG, "isCarrierOtaSpNum,ota schema pattern not right" + |
| mCarrierOtaSpNumSchema); |
| } |
| } |
| } else { |
| if (DBG) Rlog.d(LOG_TAG, "isCarrierOtaSpNum,ota schema pattern empty"); |
| } |
| return isOtaSpNum; |
| } |
| |
| /** |
| * isOTASPNumber: checks a given number against the IS-683A OTASP dial string and carrier |
| * OTASP dial string. |
| * |
| * @param dialStr the number to look up. |
| * @return true if the number is in IS-683A OTASP dial string or carrier OTASP dial string |
| */ |
| @Override |
| public boolean isOtaSpNumber(String dialStr) { |
| if (isPhoneTypeGsm()) { |
| return super.isOtaSpNumber(dialStr); |
| } else { |
| boolean isOtaSpNum = false; |
| String dialableStr = PhoneNumberUtils.extractNetworkPortionAlt(dialStr); |
| if (dialableStr != null) { |
| isOtaSpNum = isIs683OtaSpDialStr(dialableStr); |
| if (isOtaSpNum == false) { |
| isOtaSpNum = isCarrierOtaSpNum(dialableStr); |
| } |
| } |
| if (DBG) Rlog.d(LOG_TAG, "isOtaSpNumber " + isOtaSpNum); |
| return isOtaSpNum; |
| } |
| } |
| |
| @Override |
| public int getOtasp() { |
| return mSST.getOtasp(); |
| } |
| |
| @Override |
| public int getCdmaEriIconIndex() { |
| if (isPhoneTypeGsm()) { |
| return super.getCdmaEriIconIndex(); |
| } else { |
| return getServiceState().getCdmaEriIconIndex(); |
| } |
| } |
| |
| /** |
| * Returns the CDMA ERI icon mode, |
| * 0 - ON |
| * 1 - FLASHING |
| */ |
| @Override |
| public int getCdmaEriIconMode() { |
| if (isPhoneTypeGsm()) { |
| return super.getCdmaEriIconMode(); |
| } else { |
| return getServiceState().getCdmaEriIconMode(); |
| } |
| } |
| |
| /** |
| * Returns the CDMA ERI text, |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @Override |
| public String getCdmaEriText() { |
| if (isPhoneTypeGsm()) { |
| return super.getCdmaEriText(); |
| } else { |
| int roamInd = getServiceState().getCdmaRoamingIndicator(); |
| int defRoamInd = getServiceState().getCdmaDefaultRoamingIndicator(); |
| return mSST.getCdmaEriText(roamInd, defRoamInd); |
| } |
| } |
| |
| // Return true if either CSIM or RUIM app is present |
| @Override |
| public boolean isCdmaSubscriptionAppPresent() { |
| UiccCardApplication cdmaApplication = |
| mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP2); |
| return cdmaApplication != null && (cdmaApplication.getType() == AppType.APPTYPE_CSIM || |
| cdmaApplication.getType() == AppType.APPTYPE_RUIM); |
| } |
| |
| protected void phoneObjectUpdater(int newVoiceRadioTech) { |
| logd("phoneObjectUpdater: newVoiceRadioTech=" + newVoiceRadioTech); |
| |
| // Check for a voice over LTE/NR replacement |
| if (ServiceState.isPsOnlyTech(newVoiceRadioTech) |
| || (newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)) { |
| CarrierConfigManager configMgr = (CarrierConfigManager) |
| getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle b = configMgr.getConfigForSubId(getSubId()); |
| if (b != null) { |
| int volteReplacementRat = |
| b.getInt(CarrierConfigManager.KEY_VOLTE_REPLACEMENT_RAT_INT); |
| logd("phoneObjectUpdater: volteReplacementRat=" + volteReplacementRat); |
| if (volteReplacementRat != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN && |
| //In cdma case, replace rat only if csim or ruim app present |
| (ServiceState.isGsm(volteReplacementRat) || |
| isCdmaSubscriptionAppPresent())) { |
| newVoiceRadioTech = volteReplacementRat; |
| } |
| } else { |
| loge("phoneObjectUpdater: didn't get volteReplacementRat from carrier config"); |
| } |
| } |
| |
| if(mRilVersion == 6 && getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE) { |
| /* |
| * On v6 RIL, when LTE_ON_CDMA is TRUE, always create CDMALTEPhone |
| * irrespective of the voice radio tech reported. |
| */ |
| if (getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { |
| logd("phoneObjectUpdater: LTE ON CDMA property is set. Use CDMA Phone" + |
| " newVoiceRadioTech=" + newVoiceRadioTech + |
| " mActivePhone=" + getPhoneName()); |
| return; |
| } else { |
| logd("phoneObjectUpdater: LTE ON CDMA property is set. Switch to CDMALTEPhone" + |
| " newVoiceRadioTech=" + newVoiceRadioTech + |
| " mActivePhone=" + getPhoneName()); |
| newVoiceRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT; |
| } |
| } else { |
| |
| // If the device is shutting down, then there is no need to switch to the new phone |
| // which might send unnecessary attach request to the modem. |
| if (isShuttingDown()) { |
| logd("Device is shutting down. No need to switch phone now."); |
| return; |
| } |
| |
| boolean matchCdma = ServiceState.isCdma(newVoiceRadioTech); |
| boolean matchGsm = ServiceState.isGsm(newVoiceRadioTech); |
| if ((matchCdma && getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) || |
| (matchGsm && getPhoneType() == PhoneConstants.PHONE_TYPE_GSM)) { |
| // Nothing changed. Keep phone as it is. |
| logd("phoneObjectUpdater: No change ignore," + |
| " newVoiceRadioTech=" + newVoiceRadioTech + |
| " mActivePhone=" + getPhoneName()); |
| return; |
| } |
| if (!matchCdma && !matchGsm) { |
| loge("phoneObjectUpdater: newVoiceRadioTech=" + newVoiceRadioTech + |
| " doesn't match either CDMA or GSM - error! No phone change"); |
| return; |
| } |
| } |
| |
| if (newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) { |
| // We need some voice phone object to be active always, so never |
| // delete the phone without anything to replace it with! |
| logd("phoneObjectUpdater: Unknown rat ignore, " |
| + " newVoiceRadioTech=Unknown. mActivePhone=" + getPhoneName()); |
| return; |
| } |
| |
| boolean oldPowerState = false; // old power state to off |
| if (mResetModemOnRadioTechnologyChange) { |
| if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON) { |
| oldPowerState = true; |
| logd("phoneObjectUpdater: Setting Radio Power to Off"); |
| mCi.setRadioPower(false, null); |
| } |
| } |
| |
| switchVoiceRadioTech(newVoiceRadioTech); |
| |
| if (mResetModemOnRadioTechnologyChange && oldPowerState) { // restore power state |
| logd("phoneObjectUpdater: Resetting Radio"); |
| mCi.setRadioPower(oldPowerState, null); |
| } |
| |
| // update voice radio tech in UiccProfile |
| UiccProfile uiccProfile = getUiccProfile(); |
| if (uiccProfile != null) { |
| uiccProfile.setVoiceRadioTech(newVoiceRadioTech); |
| } |
| |
| // Send an Intent to the PhoneApp that we had a radio technology change |
| Intent intent = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); |
| intent.putExtra(PhoneConstants.PHONE_NAME_KEY, getPhoneName()); |
| SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhoneId); |
| mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); |
| } |
| |
| private void switchVoiceRadioTech(int newVoiceRadioTech) { |
| |
| String outgoingPhoneName = getPhoneName(); |
| |
| logd("Switching Voice Phone : " + outgoingPhoneName + " >>> " |
| + (ServiceState.isGsm(newVoiceRadioTech) ? "GSM" : "CDMA")); |
| |
| if (ServiceState.isCdma(newVoiceRadioTech)) { |
| UiccCardApplication cdmaApplication = |
| mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP2); |
| if (cdmaApplication != null && cdmaApplication.getType() == AppType.APPTYPE_RUIM) { |
| switchPhoneType(PhoneConstants.PHONE_TYPE_CDMA); |
| } else { |
| switchPhoneType(PhoneConstants.PHONE_TYPE_CDMA_LTE); |
| } |
| } else if (ServiceState.isGsm(newVoiceRadioTech)) { |
| switchPhoneType(PhoneConstants.PHONE_TYPE_GSM); |
| } else { |
| loge("deleteAndCreatePhone: newVoiceRadioTech=" + newVoiceRadioTech + |
| " is not CDMA or GSM (error) - aborting!"); |
| } |
| } |
| |
| @Override |
| public void setLinkCapacityReportingCriteria(int[] dlThresholds, int[] ulThresholds, int ran) { |
| mCi.setLinkCapacityReportingCriteria(REPORTING_HYSTERESIS_MILLIS, REPORTING_HYSTERESIS_KBPS, |
| REPORTING_HYSTERESIS_KBPS, dlThresholds, ulThresholds, ran, null); |
| } |
| |
| @Override |
| public IccSmsInterfaceManager getIccSmsInterfaceManager(){ |
| return mIccSmsInterfaceManager; |
| } |
| |
| @Override |
| public void updatePhoneObject(int voiceRadioTech) { |
| logd("updatePhoneObject: radioTechnology=" + voiceRadioTech); |
| sendMessage(obtainMessage(EVENT_UPDATE_PHONE_OBJECT, voiceRadioTech, 0, null)); |
| } |
| |
| @Override |
| public void setImsRegistrationState(boolean registered) { |
| mSST.setImsRegistrationState(registered); |
| mCallWaitingController.setImsRegistrationState(registered); |
| } |
| |
| @Override |
| public boolean getIccRecordsLoaded() { |
| UiccProfile uiccProfile = getUiccProfile(); |
| return uiccProfile != null && uiccProfile.getIccRecordsLoaded(); |
| } |
| |
| @Override |
| public IccCard getIccCard() { |
| // This function doesn't return null for backwards compatability purposes. |
| // To differentiate between cases where SIM is absent vs. unknown we return a placeholder |
| // IccCard with the sim state set. |
| IccCard card = getUiccProfile(); |
| if (card != null) { |
| return card; |
| } else { |
| UiccSlot slot = mUiccController.getUiccSlotForPhone(mPhoneId); |
| if (slot == null || slot.isStateUnknown()) { |
| return new IccCard(IccCardConstants.State.UNKNOWN); |
| } else { |
| return new IccCard(IccCardConstants.State.ABSENT); |
| } |
| } |
| } |
| |
| private UiccProfile getUiccProfile() { |
| return UiccController.getInstance().getUiccProfileForPhone(mPhoneId); |
| } |
| |
| @Override |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| pw.println("GsmCdmaPhone extends:"); |
| super.dump(fd, pw, args); |
| pw.println(" mPrecisePhoneType=" + mPrecisePhoneType); |
| pw.println(" mCT=" + mCT); |
| pw.println(" mSST=" + mSST); |
| pw.println(" mPendingMMIs=" + mPendingMMIs); |
| pw.println(" mIccPhoneBookIntManager=" + mIccPhoneBookIntManager); |
| pw.println(" mImei=" + pii(mImei)); |
| pw.println(" mImeiSv=" + pii(mImeiSv)); |
| pw.println(" mVmNumber=" + pii(mVmNumber)); |
| pw.println(" mCdmaSSM=" + mCdmaSSM); |
| pw.println(" mCdmaSubscriptionSource=" + mCdmaSubscriptionSource); |
| pw.println(" mWakeLock=" + mWakeLock); |
| pw.println(" isInEcm()=" + isInEcm()); |
| pw.println(" mEsn=" + pii(mEsn)); |
| pw.println(" mMeid=" + pii(mMeid)); |
| pw.println(" mCarrierOtaSpNumSchema=" + mCarrierOtaSpNumSchema); |
| if (!isPhoneTypeGsm()) { |
| pw.println(" getCdmaEriIconIndex()=" + getCdmaEriIconIndex()); |
| pw.println(" getCdmaEriIconMode()=" + getCdmaEriIconMode()); |
| pw.println(" getCdmaEriText()=" + getCdmaEriText()); |
| pw.println(" isMinInfoReady()=" + isMinInfoReady()); |
| } |
| pw.println(" isCspPlmnEnabled()=" + isCspPlmnEnabled()); |
| pw.println(" mManualNetworkSelectionPlmn=" + mManualNetworkSelectionPlmn); |
| pw.println( |
| " mTelecomVoiceServiceStateOverride=" + mTelecomVoiceServiceStateOverride + "(" |
| + ServiceState.rilServiceStateToString(mTelecomVoiceServiceStateOverride) |
| + ")"); |
| pw.println(" mUiccApplicationsEnabled=" + mUiccApplicationsEnabled); |
| pw.flush(); |
| try { |
| mCallWaitingController.dump(pw); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| pw.flush(); |
| try { |
| mCellBroadcastConfigTracker.dump(fd, pw, args); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| pw.flush(); |
| } |
| |
| @Override |
| public boolean setOperatorBrandOverride(String brand) { |
| if (mUiccController == null) { |
| return false; |
| } |
| |
| UiccPort port = mUiccController.getUiccPort(getPhoneId()); |
| if (port == null) { |
| return false; |
| } |
| |
| boolean status = port.setOperatorBrandOverride(brand); |
| |
| // Refresh. |
| if (status) { |
| TelephonyManager.from(mContext).setSimOperatorNameForPhone( |
| getPhoneId(), mSST.getServiceProviderName()); |
| // TODO: check if pollState is need when set operator brand override. |
| mSST.pollState(); |
| } |
| return status; |
| } |
| |
| /** |
| * This allows a short number to be remapped to a test emergency number for testing how the |
| * frameworks handles Emergency Callback Mode without actually calling an emergency number. |
| * |
| * This is not a full test and is not a substitute for testing real emergency |
| * numbers but can be useful. |
| * |
| * To use this feature, first set a test emergency number using |
| * adb shell cmd phone emergency-number-test-mode -a 1-555-555-1212 |
| * |
| * and then set the system property ril.test.emergencynumber to a pair of |
| * numbers separated by a colon. If the first number matches the number parameter |
| * this routine returns the second number. Example: |
| * |
| * ril.test.emergencynumber=411:1-555-555-1212 |
| * |
| * To test Dial 411 take call then hang up on MO device to enter ECM. |
| * |
| * @param dialString to test if it should be remapped |
| * @return the same number or the remapped number. |
| */ |
| private String checkForTestEmergencyNumber(String dialString) { |
| String testEn = SystemProperties.get("ril.test.emergencynumber"); |
| if (!TextUtils.isEmpty(testEn)) { |
| String[] values = testEn.split(":"); |
| logd("checkForTestEmergencyNumber: values.length=" + values.length); |
| if (values.length == 2) { |
| if (values[0].equals(PhoneNumberUtils.stripSeparators(dialString))) { |
| logd("checkForTestEmergencyNumber: remap " + dialString + " to " + values[1]); |
| dialString = values[1]; |
| } |
| } |
| } |
| return dialString; |
| } |
| |
| @Override |
| @NonNull |
| public String getOperatorNumeric() { |
| String operatorNumeric = null; |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| if (r != null) { |
| operatorNumeric = r.getOperatorNumeric(); |
| } |
| } else { //isPhoneTypeCdmaLte() |
| IccRecords curIccRecords = null; |
| if (mCdmaSubscriptionSource == CDMA_SUBSCRIPTION_NV) { |
| operatorNumeric = SystemProperties.get("ro.cdma.home.operator.numeric"); |
| } else if (mCdmaSubscriptionSource == CDMA_SUBSCRIPTION_RUIM_SIM) { |
| UiccCardApplication uiccCardApplication = mUiccApplication.get(); |
| if (uiccCardApplication != null |
| && uiccCardApplication.getType() == AppType.APPTYPE_RUIM) { |
| logd("Legacy RUIM app present"); |
| curIccRecords = mIccRecords.get(); |
| } else { |
| // Use sim-records for SimApp, USimApp, CSimApp and ISimApp. |
| curIccRecords = mSimRecords; |
| } |
| if (curIccRecords != null && curIccRecords == mSimRecords) { |
| operatorNumeric = curIccRecords.getOperatorNumeric(); |
| } else { |
| curIccRecords = mIccRecords.get(); |
| if (curIccRecords != null && (curIccRecords instanceof RuimRecords)) { |
| RuimRecords csim = (RuimRecords) curIccRecords; |
| operatorNumeric = csim.getRUIMOperatorNumeric(); |
| } |
| } |
| } |
| if (operatorNumeric == null) { |
| loge("getOperatorNumeric: Cannot retrieve operatorNumeric:" |
| + " mCdmaSubscriptionSource = " + mCdmaSubscriptionSource + |
| " mIccRecords = " + ((curIccRecords != null) ? |
| curIccRecords.getRecordsLoaded() : null)); |
| } |
| |
| logd("getOperatorNumeric: mCdmaSubscriptionSource = " + mCdmaSubscriptionSource |
| + " operatorNumeric = " + operatorNumeric); |
| |
| } |
| return TextUtils.emptyIfNull(operatorNumeric); |
| } |
| |
| /** |
| * @return The country ISO for the subscription associated with this phone. |
| */ |
| public String getCountryIso() { |
| int subId = getSubId(); |
| SubscriptionInfo subInfo = SubscriptionManager.from(getContext()) |
| .getActiveSubscriptionInfo(subId); |
| if (subInfo == null || TextUtils.isEmpty(subInfo.getCountryIso())) { |
| return null; |
| } |
| return subInfo.getCountryIso().toUpperCase(Locale.ROOT); |
| } |
| |
| public void notifyEcbmTimerReset(Boolean flag) { |
| mEcmTimerResetRegistrants.notifyResult(flag); |
| } |
| |
| private static final int[] VOICE_PS_CALL_RADIO_TECHNOLOGY = { |
| ServiceState.RIL_RADIO_TECHNOLOGY_LTE, |
| ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA, |
| ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, |
| ServiceState.RIL_RADIO_TECHNOLOGY_NR |
| }; |
| |
| /** |
| * Calculates current RIL voice radio technology for CS calls. |
| * |
| * This function should only be used in {@link com.android.internal.telephony.GsmCdmaConnection} |
| * to indicate current CS call radio technology. |
| * |
| * @return the RIL voice radio technology used for CS calls, |
| * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}. |
| */ |
| public @RilRadioTechnology int getCsCallRadioTech() { |
| int calcVrat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN; |
| if (mSST != null) { |
| calcVrat = getCsCallRadioTech(mSST.mSS.getState(), |
| mSST.mSS.getRilVoiceRadioTechnology()); |
| } |
| |
| return calcVrat; |
| } |
| |
| /** |
| * Calculates current RIL voice radio technology for CS calls based on current voice |
| * registration state and technology. |
| * |
| * Mark current RIL voice radio technology as unknow when any of below condtion is met: |
| * 1) Current RIL voice registration state is not in-service. |
| * 2) Current RIL voice radio technology is PS call technology, which means CSFB will |
| * happen later after call connection is established. |
| * It is inappropriate to notify upper layer the PS call technology while current call |
| * is CS call, so before CSFB happens, mark voice radio technology as unknow. |
| * After CSFB happens, {@link #onVoiceRegStateOrRatChanged} will update voice call radio |
| * technology with correct value. |
| * |
| * @param vrs the voice registration state |
| * @param vrat the RIL voice radio technology |
| * |
| * @return the RIL voice radio technology used for CS calls, |
| * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}. |
| */ |
| private @RilRadioTechnology int getCsCallRadioTech(int vrs, int vrat) { |
| logd("getCsCallRadioTech, current vrs=" + vrs + ", vrat=" + vrat); |
| int calcVrat = vrat; |
| if (vrs != ServiceState.STATE_IN_SERVICE |
| || ArrayUtils.contains(VOICE_PS_CALL_RADIO_TECHNOLOGY, vrat)) { |
| calcVrat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN; |
| } |
| |
| logd("getCsCallRadioTech, result calcVrat=" + calcVrat); |
| return calcVrat; |
| } |
| |
| /** |
| * Handler of RIL Voice Radio Technology changed event. |
| */ |
| private void onVoiceRegStateOrRatChanged(int vrs, int vrat) { |
| if (!hasCalling()) return; |
| logd("onVoiceRegStateOrRatChanged"); |
| mCT.dispatchCsCallRadioTech(getCsCallRadioTech(vrs, vrat)); |
| } |
| |
| /** |
| * Registration point for Ecm timer reset |
| * |
| * @param h handler to notify |
| * @param what User-defined message code |
| * @param obj placed in Message.obj |
| */ |
| @Override |
| public void registerForEcmTimerReset(Handler h, int what, Object obj) { |
| mEcmTimerResetRegistrants.addUnique(h, what, obj); |
| } |
| |
| @Override |
| public void unregisterForEcmTimerReset(Handler h) { |
| mEcmTimerResetRegistrants.remove(h); |
| } |
| |
| @Override |
| public void registerForVolteSilentRedial(Handler h, int what, Object obj) { |
| mVolteSilentRedialRegistrants.addUnique(h, what, obj); |
| } |
| |
| @Override |
| public void unregisterForVolteSilentRedial(Handler h) { |
| mVolteSilentRedialRegistrants.remove(h); |
| } |
| |
| public void notifyVolteSilentRedial(String dialString, int causeCode) { |
| logd("notifyVolteSilentRedial: dialString=" + dialString + " causeCode=" + causeCode); |
| AsyncResult ar = new AsyncResult(null, |
| new SilentRedialParam(dialString, causeCode, mDialArgs), null); |
| mVolteSilentRedialRegistrants.notifyRegistrants(ar); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void registerForEmergencyDomainSelected( |
| @NonNull Handler h, int what, @Nullable Object obj) { |
| mEmergencyDomainSelectedRegistrants.addUnique(h, what, obj); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void unregisterForEmergencyDomainSelected(@NonNull Handler h) { |
| mEmergencyDomainSelectedRegistrants.remove(h); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void notifyEmergencyDomainSelected(@TransportType int transportType) { |
| logd("notifyEmergencyDomainSelected transportType=" + transportType); |
| mEmergencyDomainSelectedRegistrants.notifyRegistrants( |
| new AsyncResult(null, transportType, null)); |
| } |
| |
| /** |
| * Sets the SIM voice message waiting indicator records. |
| * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported |
| * @param countWaiting The number of messages waiting, if known. Use |
| * -1 to indicate that an unknown number of |
| * messages are waiting |
| */ |
| @Override |
| public void setVoiceMessageWaiting(int line, int countWaiting) { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| if (r != null) { |
| r.setVoiceMessageWaiting(line, countWaiting); |
| } else { |
| logd("SIM Records not found, MWI not updated"); |
| } |
| } else { |
| setVoiceMessageCount(countWaiting); |
| } |
| } |
| |
| private CallForwardInfo[] makeEmptyCallForward() { |
| CallForwardInfo infos[] = new CallForwardInfo[1]; |
| |
| infos[0] = new CallForwardInfo(); |
| infos[0].status = CommandsInterface.SS_STATUS_UNKNOWN; |
| infos[0].reason = 0; |
| infos[0].serviceClass = CommandsInterface.SERVICE_CLASS_VOICE; |
| infos[0].toa = PhoneNumberUtils.TOA_Unknown; |
| infos[0].number = ""; |
| infos[0].timeSeconds = 0; |
| |
| return infos; |
| } |
| |
| private PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) { |
| final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class); |
| final TelephonyManager telephonyManager = TelephonyManager.from(mContext); |
| final Iterator<PhoneAccountHandle> phoneAccounts = |
| telecomManager.getCallCapablePhoneAccounts(true).listIterator(); |
| |
| while (phoneAccounts.hasNext()) { |
| final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next(); |
| final PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle); |
| if (subId == telephonyManager.getSubIdForPhoneAccount(phoneAccount)) { |
| return phoneAccountHandle; |
| } |
| } |
| |
| return null; |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private void logd(String s) { |
| Rlog.d(LOG_TAG, "[" + mPhoneId + "] " + s); |
| } |
| |
| private void logi(String s) { |
| Rlog.i(LOG_TAG, "[" + mPhoneId + "] " + s); |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private void loge(String s) { |
| Rlog.e(LOG_TAG, "[" + mPhoneId + "] " + s); |
| } |
| |
| private static String pii(String s) { |
| return Rlog.pii(LOG_TAG, s); |
| } |
| |
| @Override |
| public boolean isUtEnabled() { |
| Phone imsPhone = mImsPhone; |
| if (imsPhone != null) { |
| return imsPhone.isUtEnabled(); |
| } else { |
| logd("isUtEnabled: called for GsmCdma"); |
| return false; |
| } |
| } |
| |
| public String getDtmfToneDelayKey() { |
| return isPhoneTypeGsm() ? |
| CarrierConfigManager.KEY_GSM_DTMF_TONE_DELAY_INT : |
| CarrierConfigManager.KEY_CDMA_DTMF_TONE_DELAY_INT; |
| } |
| |
| @VisibleForTesting |
| public PowerManager.WakeLock getWakeLock() { |
| return mWakeLock; |
| } |
| |
| public int getLteOnCdmaMode() { |
| int currentConfig = TelephonyProperties.lte_on_cdma_device() |
| .orElse(PhoneConstants.LTE_ON_CDMA_FALSE); |
| int lteOnCdmaModeDynamicValue = currentConfig; |
| |
| UiccCardApplication cdmaApplication = |
| mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP2); |
| if (cdmaApplication != null && cdmaApplication.getType() == AppType.APPTYPE_RUIM) { |
| //Legacy RUIM cards don't support LTE. |
| lteOnCdmaModeDynamicValue = RILConstants.LTE_ON_CDMA_FALSE; |
| |
| //Override only if static configuration is TRUE. |
| if (currentConfig == RILConstants.LTE_ON_CDMA_TRUE) { |
| return lteOnCdmaModeDynamicValue; |
| } |
| } |
| return currentConfig; |
| } |
| |
| private void updateTtyMode(int ttyMode) { |
| logi(String.format("updateTtyMode ttyMode=%d", ttyMode)); |
| setTTYMode(telecomModeToPhoneMode(ttyMode), null); |
| } |
| private void updateUiTtyMode(int ttyMode) { |
| logi(String.format("updateUiTtyMode ttyMode=%d", ttyMode)); |
| setUiTTYMode(telecomModeToPhoneMode(ttyMode), null); |
| } |
| |
| /** |
| * Given a telecom TTY mode, convert to a Telephony mode equivalent. |
| * @param telecomMode Telecom TTY mode. |
| * @return Telephony phone TTY mode. |
| */ |
| private static int telecomModeToPhoneMode(int telecomMode) { |
| switch (telecomMode) { |
| // AT command only has 0 and 1, so mapping VCO |
| // and HCO to FULL |
| case TelecomManager.TTY_MODE_FULL: |
| case TelecomManager.TTY_MODE_VCO: |
| case TelecomManager.TTY_MODE_HCO: |
| return Phone.TTY_MODE_FULL; |
| default: |
| return Phone.TTY_MODE_OFF; |
| } |
| } |
| |
| /** |
| * Load the current TTY mode in GsmCdmaPhone based on Telecom and UI settings. |
| */ |
| private void loadTtyMode() { |
| if (!hasCalling()) return; |
| |
| int ttyMode = TelecomManager.TTY_MODE_OFF; |
| TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class); |
| if (telecomManager != null) { |
| ttyMode = telecomManager.getCurrentTtyMode(); |
| } |
| updateTtyMode(ttyMode); |
| //Get preferred TTY mode from settings as UI Tty mode is always user preferred Tty mode. |
| ttyMode = Settings.Secure.getInt(mContext.getContentResolver(), |
| Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF); |
| updateUiTtyMode(ttyMode); |
| } |
| |
| private void reapplyUiccAppsEnablementIfNeeded(int retries) { |
| UiccSlot slot = mUiccController.getUiccSlotForPhone(mPhoneId); |
| |
| // If no card is present or we don't have mUiccApplicationsEnabled yet, do nothing. |
| if (slot == null || slot.getCardState() != IccCardStatus.CardState.CARDSTATE_PRESENT |
| || mUiccApplicationsEnabled == null) { |
| loge("reapplyUiccAppsEnablementIfNeeded: slot state=" |
| + (slot != null ? slot.getCardState() : null)); |
| return; |
| } |
| |
| // Due to timing issue, sometimes UiccPort is coming null, so don't use UiccPort object |
| // to retrieve the iccId here. Instead, depend on the UiccSlot API. |
| String iccId = slot.getIccId(slot.getPortIndexFromPhoneId(mPhoneId)); |
| if (iccId == null) { |
| loge("reapplyUiccAppsEnablementIfNeeded iccId is null, phoneId: " + mPhoneId |
| + " portIndex: " + slot.getPortIndexFromPhoneId(mPhoneId)); |
| return; |
| } |
| |
| SubscriptionInfo info = mSubscriptionManagerService |
| .getAllSubInfoList(mContext.getOpPackageName(), mContext.getAttributionTag()) |
| .stream() |
| .filter(subInfo -> subInfo.getIccId().equals(IccUtils.stripTrailingFs(iccId))) |
| .findFirst() |
| .orElse(null); |
| |
| logd("reapplyUiccAppsEnablementIfNeeded: retries=" + retries + ", subInfo=" + info); |
| |
| // If info is null, it could be a new subscription. By default we enable it. |
| boolean expectedValue = info == null || info.areUiccApplicationsEnabled(); |
| |
| // If for any reason current state is different from configured state, re-apply the |
| // configured state. |
| if (expectedValue != mUiccApplicationsEnabled) { |
| mCi.enableUiccApplications(expectedValue, Message.obtain( |
| this, EVENT_REAPPLY_UICC_APPS_ENABLEMENT_DONE, |
| new Pair<>(expectedValue, retries))); |
| } |
| } |
| |
| // Enable or disable uicc applications. |
| @Override |
| public void enableUiccApplications(boolean enable, Message onCompleteMessage) { |
| // First check if card is present. Otherwise mUiccApplicationsDisabled doesn't make |
| // any sense. |
| UiccSlot slot = mUiccController.getUiccSlotForPhone(mPhoneId); |
| if (slot == null || slot.getCardState() != IccCardStatus.CardState.CARDSTATE_PRESENT) { |
| if (onCompleteMessage != null) { |
| AsyncResult.forMessage(onCompleteMessage, null, |
| new IllegalStateException("No SIM card is present")); |
| onCompleteMessage.sendToTarget(); |
| } |
| return; |
| } |
| |
| mCi.enableUiccApplications(enable, onCompleteMessage); |
| } |
| |
| /** |
| * Whether disabling a physical subscription is supported or not. |
| */ |
| @Override |
| public boolean canDisablePhysicalSubscription() { |
| return mCi.canToggleUiccApplicationsEnablement(); |
| } |
| |
| @Override |
| public @NonNull List<String> getEquivalentHomePlmns() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| if (r != null && r.getEhplmns() != null) { |
| return Arrays.asList(r.getEhplmns()); |
| } |
| } else if (isPhoneTypeCdma()) { |
| loge("EHPLMN is not available in CDMA"); |
| } |
| return Collections.emptyList(); |
| } |
| |
| /** |
| * @return Currently bound data service package names. |
| */ |
| public @NonNull List<String> getDataServicePackages() { |
| return getDataNetworkController().getDataServicePackages(); |
| } |
| |
| private void updateBroadcastEmergencyCallStateChangesAfterCarrierConfigChanged( |
| @NonNull PersistableBundle config) { |
| // get broadcastEmergencyCallStateChanges |
| boolean broadcastEmergencyCallStateChanges = config.getBoolean( |
| CarrierConfigManager.KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL); |
| logd("broadcastEmergencyCallStateChanges = " + broadcastEmergencyCallStateChanges); |
| setBroadcastEmergencyCallStateChanges(broadcastEmergencyCallStateChanges); |
| } |
| |
| private void updateNrSettingsAfterCarrierConfigChanged(@NonNull PersistableBundle config) { |
| int[] nrAvailabilities = config.getIntArray( |
| CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY); |
| mIsCarrierNrSupported = !ArrayUtils.isEmpty(nrAvailabilities); |
| } |
| |
| private void updateVoNrSettings(@NonNull PersistableBundle config) { |
| if (getIccCard().getState() != IccCardConstants.State.LOADED) { |
| return; |
| } |
| |
| boolean mIsVonrEnabledByCarrier = |
| config.getBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL); |
| boolean mDefaultVonr = |
| config.getBoolean(CarrierConfigManager.KEY_VONR_ON_BY_DEFAULT_BOOL); |
| |
| int setting = -1; |
| SubscriptionInfoInternal subInfo = mSubscriptionManagerService |
| .getSubscriptionInfoInternal(getSubId()); |
| if (subInfo != null) { |
| setting = subInfo.getNrAdvancedCallingEnabled(); |
| } |
| |
| logd("VoNR setting from telephony.db:" |
| + setting |
| + " ,vonr_enabled_bool:" |
| + mIsVonrEnabledByCarrier |
| + " ,vonr_on_by_default_bool:" |
| + mDefaultVonr); |
| |
| boolean enbleVonr = mIsVonrEnabledByCarrier |
| && (setting == 1 || (setting == -1 && mDefaultVonr)); |
| mCi.setVoNrEnabled(enbleVonr, obtainMessage(EVENT_SET_VONR_ENABLED_DONE), null); |
| } |
| |
| private void updateCdmaRoamingSettingsAfterCarrierConfigChanged( |
| @NonNull PersistableBundle config) { |
| // Changing the cdma roaming settings based carrier config. |
| int config_cdma_roaming_mode = config.getInt( |
| CarrierConfigManager.KEY_CDMA_ROAMING_MODE_INT); |
| int current_cdma_roaming_mode = |
| Settings.Global.getInt(getContext().getContentResolver(), |
| Settings.Global.CDMA_ROAMING_MODE, |
| TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT); |
| switch (config_cdma_roaming_mode) { |
| // Carrier's cdma_roaming_mode will overwrite the user's previous settings |
| // Keep the user's previous setting in global variable which will be used |
| // when carrier's setting is turn off. |
| case TelephonyManager.CDMA_ROAMING_MODE_HOME: |
| case TelephonyManager.CDMA_ROAMING_MODE_AFFILIATED: |
| case TelephonyManager.CDMA_ROAMING_MODE_ANY: |
| logd("cdma_roaming_mode is going to changed to " |
| + config_cdma_roaming_mode); |
| setCdmaRoamingPreference(config_cdma_roaming_mode, |
| obtainMessage(EVENT_SET_ROAMING_PREFERENCE_DONE)); |
| break; |
| |
| // When carrier's setting is turn off, change the cdma_roaming_mode to the |
| // previous user's setting |
| case TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT: |
| if (current_cdma_roaming_mode != config_cdma_roaming_mode) { |
| logd("cdma_roaming_mode is going to changed to " |
| + current_cdma_roaming_mode); |
| setCdmaRoamingPreference(current_cdma_roaming_mode, |
| obtainMessage(EVENT_SET_ROAMING_PREFERENCE_DONE)); |
| } |
| break; |
| default: |
| loge("Invalid cdma_roaming_mode settings: " + config_cdma_roaming_mode); |
| } |
| } |
| |
| /** |
| * Determines if IMS is enabled for call. |
| * |
| * @return {@code true} if IMS calling is enabled. |
| */ |
| public boolean isImsUseEnabled() { |
| ImsManager imsManager = mImsManagerFactory.create(mContext, mPhoneId); |
| boolean imsUseEnabled = ((imsManager.isVolteEnabledByPlatform() |
| && imsManager.isEnhanced4gLteModeSettingEnabledByUser()) |
| || (imsManager.isWfcEnabledByPlatform() && imsManager.isWfcEnabledByUser()) |
| && imsManager.isNonTtyOrTtyOnVolteEnabled()); |
| return imsUseEnabled; |
| } |
| |
| @Override |
| public InboundSmsHandler getInboundSmsHandler(boolean is3gpp2) { |
| return mIccSmsInterfaceManager.getInboundSmsHandler(is3gpp2); |
| } |
| |
| /** |
| * Return current cell broadcast ranges. |
| */ |
| public List<CellBroadcastIdRange> getCellBroadcastIdRanges() { |
| return mCellBroadcastConfigTracker.getCellBroadcastIdRanges(); |
| } |
| |
| /** |
| * Set reception of cell broadcast messages with the list of the given ranges. |
| */ |
| @Override |
| public void setCellBroadcastIdRanges( |
| @NonNull List<CellBroadcastIdRange> ranges, Consumer<Integer> callback) { |
| mCellBroadcastConfigTracker.setCellBroadcastIdRanges(ranges, callback); |
| } |
| |
| /** |
| * The following function checks if supplementary service request is blocked due to FDN. |
| * @param requestType request type associated with the supplementary service |
| * @param serviceType supplementary service type |
| * @return {@code true} if request is blocked due to FDN. |
| */ |
| private boolean isRequestBlockedByFDN(SsData.RequestType requestType, |
| SsData.ServiceType serviceType) { |
| ArrayList<String> controlStrings = GsmMmiCode.getControlStrings(requestType, serviceType); |
| return FdnUtils.isSuppServiceRequestBlockedByFdn(mPhoneId, controlStrings, getCountryIso()); |
| } |
| |
| @Override |
| public void handleNullCipherEnabledChange() { |
| if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CELLULAR_SECURITY, |
| TelephonyManager.PROPERTY_ENABLE_NULL_CIPHER_TOGGLE, true)) { |
| logi("Not handling null cipher update. Feature disabled by DeviceConfig."); |
| return; |
| } |
| mCi.setNullCipherAndIntegrityEnabled( |
| getNullCipherAndIntegrityEnabledPreference(), |
| obtainMessage(EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE)); |
| } |
| |
| @Override |
| public void handleIdentifierDisclosureNotificationPreferenceChange() { |
| if (!mFeatureFlags.enableIdentifierDisclosureTransparency()) { |
| logi("Not handling identifier disclosure preference change. Feature flag " |
| + "enable_identifier_disclosure_transparency disabled"); |
| return; |
| } |
| boolean prefEnabled = getIdentifierDisclosureNotificationsPreferenceEnabled(); |
| |
| // The notifier is tied to handling unsolicited updates from the modem, not the |
| // enable/disable API, so we only toggle the enable state if the unsol events feature |
| // flag is enabled. |
| if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()) { |
| if (prefEnabled) { |
| mIdentifierDisclosureNotifier.enable(mContext); |
| } else { |
| mIdentifierDisclosureNotifier.disable(mContext); |
| } |
| } else { |
| logi("Not toggling enable state for disclosure notifier. Feature flag " |
| + "enable_identifier_disclosure_transparency_unsol_events is disabled"); |
| } |
| |
| mCi.setCellularIdentifierTransparencyEnabled(prefEnabled, |
| obtainMessage(EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE)); |
| } |
| |
| @Override |
| public void handleNullCipherNotificationPreferenceChanged() { |
| if (!mFeatureFlags.enableModemCipherTransparency()) { |
| logi("Not handling null cipher notification preference change. Feature flag " |
| + "enable_modem_cipher_transparency disabled"); |
| return; |
| } |
| boolean prefEnabled = getNullCipherNotificationsPreferenceEnabled(); |
| |
| // The notifier is tied to handling unsolicited updates from the modem, not the |
| // enable/disable API. |
| if (mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) { |
| if (prefEnabled) { |
| mNullCipherNotifier.enable(mContext); |
| } else { |
| mNullCipherNotifier.disable(mContext); |
| } |
| } else { |
| logi( |
| "Not toggling enable state for cipher notifier. Feature flag " |
| + "enable_modem_cipher_transparency_unsol_events is disabled."); |
| } |
| |
| mCi.setSecurityAlgorithmsUpdatedEnabled(prefEnabled, |
| obtainMessage(EVENT_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED_DONE)); |
| } |
| |
| /** |
| * Update the phoneId -> subId mapping of the null cipher notifier. |
| */ |
| @VisibleForTesting |
| public void updateNullCipherNotifier() { |
| if (!mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) { |
| return; |
| } |
| |
| SubscriptionInfoInternal subInfo = mSubscriptionManagerService |
| .getSubscriptionInfoInternal(getSubId()); |
| boolean active = false; |
| if (subInfo != null) { |
| active = subInfo.isActive(); |
| } |
| mNullCipherNotifier.setSubscriptionMapping(mContext, getPhoneId(), |
| active ? subInfo.getSubscriptionId() : -1); |
| } |
| |
| @Override |
| public boolean isNullCipherAndIntegritySupported() { |
| return mIsNullCipherAndIntegritySupported; |
| } |
| |
| @Override |
| public boolean isIdentifierDisclosureTransparencySupported() { |
| return mIsIdentifierDisclosureTransparencySupported; |
| } |
| |
| @Override |
| public boolean isNullCipherNotificationSupported() { |
| return mIsNullCipherNotificationSupported; |
| } |
| |
| @Override |
| public void refreshSafetySources(String refreshBroadcastId) { |
| if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents() |
| || mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) { |
| mSafetySource.refresh(mContext, refreshBroadcastId); |
| } |
| } |
| } |