/* * 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.TelephonyManager.HAL_SERVICE_RADIO; import static android.telephony.ims.ImsService.CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.BroadcastOptions; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.hardware.radio.modem.ImeiInfo; import android.net.Uri; import android.os.AsyncResult; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; import android.os.Registrant; import android.os.RegistrantList; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; import android.preference.PreferenceManager; import android.sysprop.TelephonyProperties; import android.telecom.VideoProfile; import android.telephony.AccessNetworkConstants; import android.telephony.Annotation.SrvccState; import android.telephony.CarrierConfigManager; import android.telephony.CarrierRestrictionRules; import android.telephony.CellBroadcastIdRange; import android.telephony.CellIdentity; import android.telephony.CellInfo; import android.telephony.ClientRequestStats; import android.telephony.DomainSelectionService; import android.telephony.ImsiEncryptionInfo; import android.telephony.LinkCapacityEstimate; import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneStateListener; import android.telephony.PhysicalChannelConfig; import android.telephony.PreciseDataConnectionState; import android.telephony.RadioAccessFamily; import android.telephony.RadioAccessSpecifier; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager; import android.telephony.TelephonyManager.HalService; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.RegistrationManager; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; import android.util.Xml; import com.android.ims.ImsCall; import com.android.ims.ImsConfig; import com.android.ims.ImsException; import com.android.ims.ImsManager; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.analytics.TelephonyAnalytics; import com.android.internal.telephony.data.AccessNetworksManager; import com.android.internal.telephony.data.DataNetworkController; import com.android.internal.telephony.data.DataSettingsManager; import com.android.internal.telephony.data.LinkBandwidthEstimator; import com.android.internal.telephony.domainselection.DomainSelectionResolver; import com.android.internal.telephony.emergency.EmergencyConstants; 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.imsphone.ImsCallInfo; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.imsphone.ImsPhoneCall; import com.android.internal.telephony.metrics.SmsStats; import com.android.internal.telephony.metrics.VoiceCallSessionStats; import com.android.internal.telephony.subscription.SubscriptionInfoInternal; import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.test.SimulatedRadioControl; import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType; import com.android.internal.telephony.uicc.IccFileHandler; import com.android.internal.telephony.uicc.IccRecords; import com.android.internal.telephony.uicc.IsimRecords; import com.android.internal.telephony.uicc.UiccCard; 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.UsimServiceTable; import com.android.internal.telephony.util.TelephonyUtils; import com.android.internal.util.XmlUtils; import com.android.telephony.Rlog; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.stream.Collectors; /** * (Not for SDK use) * A base implementation for the com.android.internal.telephony.Phone interface. * * Note that implementations of Phone.java are expected to be used * from a single application thread. This should be the same thread that * originally called PhoneFactory to obtain the interface. * * {@hide} * */ public abstract class Phone extends Handler implements PhoneInternalInterface { private static final String LOG_TAG = "Phone"; protected final static Object lockForRadioTechnologyChange = new Object(); protected final int USSD_MAX_QUEUE = 10; // Key used to read and write the saved network selection numeric value public static final String NETWORK_SELECTION_KEY = "network_selection_key"; // Key used to read and write the saved network selection operator name public static final String NETWORK_SELECTION_NAME_KEY = "network_selection_name_key"; // Key used to read and write the saved network selection operator short name public static final String NETWORK_SELECTION_SHORT_KEY = "network_selection_short_key"; // Key used to read/write "disable data connection on boot" pref (used for testing) public static final String DATA_DISABLED_ON_BOOT_KEY = "disabled_on_boot_key"; // Key used to read/write data_roaming_is_user_setting pref public static final String DATA_ROAMING_IS_USER_SETTING_KEY = "data_roaming_is_user_setting_key"; // Default value when there has been no last emergency SMS time recorded yet. private static final int EMERGENCY_SMS_NO_TIME_RECORDED = -1; // The max timer value that the platform can be in emergency SMS mode (5 minutes). private static final int EMERGENCY_SMS_TIMER_MAX_MS = 300000; /* Event Constants */ protected static final int EVENT_RADIO_AVAILABLE = 1; /** Supplementary Service Notification received. */ protected static final int EVENT_SSN = 2; protected static final int EVENT_SIM_RECORDS_LOADED = 3; private static final int EVENT_MMI_DONE = 4; protected static final int EVENT_RADIO_ON = 5; protected static final int EVENT_GET_BASEBAND_VERSION_DONE = 6; protected static final int EVENT_USSD = 7; @VisibleForTesting public static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 8; private static final int EVENT_GET_SIM_STATUS_DONE = 11; protected static final int EVENT_SET_CALL_FORWARD_DONE = 12; protected static final int EVENT_GET_CALL_FORWARD_DONE = 13; protected static final int EVENT_CALL_RING = 14; private static final int EVENT_CALL_RING_CONTINUE = 15; // Used to intercept the carrier selection calls so that // we can save the values. private static final int EVENT_SET_NETWORK_MANUAL_COMPLETE = 16; private static final int EVENT_SET_NETWORK_AUTOMATIC_COMPLETE = 17; protected static final int EVENT_SET_CLIR_COMPLETE = 18; protected static final int EVENT_REGISTERED_TO_NETWORK = 19; protected static final int EVENT_SET_VM_NUMBER_DONE = 20; // Events for CDMA support protected static final int EVENT_GET_DEVICE_IDENTITY_DONE = 21; protected static final int EVENT_RUIM_RECORDS_LOADED = 22; protected static final int EVENT_NV_READY = 23; private static final int EVENT_SET_ENHANCED_VP = 24; @VisibleForTesting public static final int EVENT_EMERGENCY_CALLBACK_MODE_ENTER = 25; protected static final int EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE = 26; protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 27; // other protected static final int EVENT_SET_NETWORK_AUTOMATIC = 28; protected static final int EVENT_ICC_RECORD_EVENTS = 29; @VisibleForTesting protected static final int EVENT_ICC_CHANGED = 30; // Single Radio Voice Call Continuity @VisibleForTesting protected static final int EVENT_SRVCC_STATE_CHANGED = 31; private static final int EVENT_INITIATE_SILENT_REDIAL = 32; private static final int EVENT_RADIO_NOT_AVAILABLE = 33; private static final int EVENT_UNSOL_OEM_HOOK_RAW = 34; protected static final int EVENT_GET_RADIO_CAPABILITY = 35; protected static final int EVENT_SS = 36; private static final int EVENT_CHECK_FOR_NETWORK_AUTOMATIC = 38; protected static final int EVENT_VOICE_RADIO_TECH_CHANGED = 39; protected static final int EVENT_REQUEST_VOICE_RADIO_TECH_DONE = 40; protected static final int EVENT_RIL_CONNECTED = 41; protected static final int EVENT_UPDATE_PHONE_OBJECT = 42; protected static final int EVENT_CARRIER_CONFIG_CHANGED = 43; // Carrier's CDMA prefer mode setting protected static final int EVENT_SET_ROAMING_PREFERENCE_DONE = 44; protected static final int EVENT_MODEM_RESET = 45; protected static final int EVENT_VRS_OR_RAT_CHANGED = 46; // Radio state change protected static final int EVENT_RADIO_STATE_CHANGED = 47; protected static final int EVENT_SET_CARRIER_DATA_ENABLED = 48; protected static final int EVENT_GET_AVAILABLE_NETWORKS_DONE = 51; protected static final int EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED = 53; protected static final int EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED = 54; protected static final int EVENT_GET_UICC_APPS_ENABLEMENT_DONE = 55; protected static final int EVENT_REAPPLY_UICC_APPS_ENABLEMENT_DONE = 56; protected static final int EVENT_REGISTRATION_FAILED = 57; protected static final int EVENT_BARRING_INFO_CHANGED = 58; protected static final int EVENT_LINK_CAPACITY_CHANGED = 59; protected static final int EVENT_RESET_CARRIER_KEY_IMSI_ENCRYPTION = 60; protected static final int EVENT_SET_VONR_ENABLED_DONE = 61; protected static final int EVENT_SUBSCRIPTIONS_CHANGED = 62; protected static final int EVENT_GET_USAGE_SETTING_DONE = 63; protected static final int EVENT_SET_USAGE_SETTING_DONE = 64; protected static final int EVENT_IMS_DEREGISTRATION_TRIGGERED = 65; protected static final int EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE = 66; protected static final int EVENT_GET_DEVICE_IMEI_DONE = 67; protected static final int EVENT_TRIGGER_NOTIFY_ANBR = 68; protected static final int EVENT_GET_N1_MODE_ENABLED_DONE = 69; protected static final int EVENT_SET_N1_MODE_ENABLED_DONE = 70; protected static final int EVENT_IMEI_MAPPING_CHANGED = 71; protected static final int EVENT_CELL_IDENTIFIER_DISCLOSURE = 72; protected static final int EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE = 73; protected static final int EVENT_SECURITY_ALGORITHM_UPDATE = 74; protected static final int EVENT_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED_DONE = 75; protected static final int EVENT_LAST = EVENT_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED_DONE; // For shared prefs. private static final String GSM_ROAMING_LIST_OVERRIDE_PREFIX = "gsm_roaming_list_"; private static final String GSM_NON_ROAMING_LIST_OVERRIDE_PREFIX = "gsm_non_roaming_list_"; private static final String CDMA_ROAMING_LIST_OVERRIDE_PREFIX = "cdma_roaming_list_"; private static final String CDMA_NON_ROAMING_LIST_OVERRIDE_PREFIX = "cdma_non_roaming_list_"; // Key used to read/write current CLIR setting public static final String CLIR_KEY = "clir_sub_key"; // Key used for storing voice mail count private static final String VM_COUNT = "vm_count_key"; // Key used to read/write the ID for storing the voice mail private static final String VM_ID = "vm_id_key"; // Key used for storing call forwarding status public static final String CF_STATUS = "cf_status_key"; // Key used to read/write the ID for storing the call forwarding status public static final String CF_ID = "cf_id_key"; // Key used to read/write "disable DNS server check" pref (used for testing) private static final String DNS_SERVER_CHECK_DISABLED_KEY = "dns_server_check_disabled_key"; // Integer used to let the calling application know that the we are ignoring auto mode switch. private static final int ALREADY_IN_AUTO_SELECTION = 1; public static final String PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED = "pref_null_cipher_and_integrity_enabled"; private final TelephonyAdminReceiver m2gAdminUpdater; public static final String PREF_IDENTIFIER_DISCLOSURE_NOTIFICATIONS_ENABLED = "pref_identifier_disclosure_notifications_enabled"; public static final String PREF_NULL_CIPHER_NOTIFICATIONS_ENABLED = "pref_null_cipher_notifications_enabled"; protected final FeatureFlags mFeatureFlags; /** * This method is invoked when the Phone exits Emergency Callback Mode. */ protected void handleExitEmergencyCallbackMode() { } /** * Small container class used to hold information relevant to * the carrier selection process. operatorNumeric can be "" * if we are looking for automatic selection. operatorAlphaLong is the * corresponding operator name. */ protected static class NetworkSelectMessage { public Message message; public String operatorNumeric; public String operatorAlphaLong; public String operatorAlphaShort; } public static class SilentRedialParam { public String dialString; public int causeCode; public DialArgs dialArgs; public SilentRedialParam(String dialString, int causeCode, DialArgs dialArgs) { this.dialString = dialString; this.causeCode = causeCode; this.dialArgs = dialArgs; } } /* Instance Variables */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public CommandsInterface mCi; protected int mVmCount = 0; private boolean mDnsCheckDisabled; protected DataNetworkController mDataNetworkController; /* Used for dispatching signals to configured carrier apps */ protected CarrierSignalAgent mCarrierSignalAgent; /* Used for dispatching carrier action from carrier apps */ protected CarrierActionAgent mCarrierActionAgent; private boolean mDoesRilSendMultipleCallRing; private int mCallRingContinueToken; private int mCallRingDelay; private boolean mIsVoiceCapable = true; private final AppSmsManager mAppSmsManager; private SimActivationTracker mSimActivationTracker; // Keep track of whether or not the phone is in Emergency Callback Mode for Phone and // subclasses protected boolean mIsPhoneInEcmState = false; // Keep track of the case where ECM was cancelled to place another outgoing emergency call. // We will need to restart it after the emergency call ends. protected boolean mEcmCanceledForEmergency = false; private volatile long mTimeLastEmergencySmsSentMs = EMERGENCY_SMS_NO_TIME_RECORDED; // Variable to cache the video capability. When RAT changes, we lose this info and are unable // to recover from the state. We cache it and notify listeners when they register. protected boolean mIsVideoCapable = false; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected UiccController mUiccController = null; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected final AtomicReference mIccRecords = new AtomicReference(); @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public SmsStorageMonitor mSmsStorageMonitor; public SmsUsageMonitor mSmsUsageMonitor; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected AtomicReference mUiccApplication = new AtomicReference(); private TelephonyTester mTelephonyTester; private String mName; private final String mActionDetached; private final String mActionAttached; protected DeviceStateMonitor mDeviceStateMonitor; protected DisplayInfoController mDisplayInfoController; protected AccessNetworksManager mAccessNetworksManager; // Used for identify the carrier of current subscription protected CarrierResolver mCarrierResolver; protected SignalStrengthController mSignalStrengthController; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected int mPhoneId; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected Phone mImsPhone = null; private final AtomicReference mRadioCapability = new AtomicReference(); protected TelephonyComponentFactory mTelephonyComponentFactory; private int mPreferredUsageSetting = SubscriptionManager.USAGE_SETTING_UNKNOWN; private int mUsageSettingFromModem = SubscriptionManager.USAGE_SETTING_UNKNOWN; private boolean mIsUsageSettingSupported = true; //IMS /** * {@link CallStateException} message text used to indicate that an IMS call has failed because * it needs to be retried using GSM or CDMA (e.g. CS fallback). * TODO: Replace this with a proper exception; {@link CallStateException} doesn't make sense. */ public static final String CS_FALLBACK = "cs_fallback"; // Used for retry over cs for supplementary services public static final String CS_FALLBACK_SS = "cs_fallback_ss"; /** * @deprecated Use {@link android.telephony.ims.ImsManager#EXTRA_WFC_REGISTRATION_FAILURE_TITLE} * instead. */ @Deprecated public static final String EXTRA_KEY_ALERT_TITLE = android.telephony.ims.ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_TITLE; /** * @deprecated Use * {@link android.telephony.ims.ImsManager#EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE} instead. */ @Deprecated public static final String EXTRA_KEY_ALERT_MESSAGE = android.telephony.ims.ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE; public static final String EXTRA_KEY_ALERT_SHOW = "alertShow"; public static final String EXTRA_KEY_NOTIFICATION_MESSAGE = "notificationMessage"; protected final RegistrantList mPreciseCallStateRegistrants = new RegistrantList(); private final RegistrantList mHandoverRegistrants = new RegistrantList(); private final RegistrantList mNewRingingConnectionRegistrants = new RegistrantList(); private final RegistrantList mIncomingRingRegistrants = new RegistrantList(); protected final RegistrantList mDisconnectRegistrants = new RegistrantList(); private final RegistrantList mServiceStateRegistrants = new RegistrantList(); protected final RegistrantList mMmiCompleteRegistrants = new RegistrantList(); @UnsupportedAppUsage protected final RegistrantList mMmiRegistrants = new RegistrantList(); protected final RegistrantList mUnknownConnectionRegistrants = new RegistrantList(); protected final RegistrantList mSuppServiceFailedRegistrants = new RegistrantList(); protected final RegistrantList mRadioOffOrNotAvailableRegistrants = new RegistrantList(); protected final RegistrantList mSimRecordsLoadedRegistrants = new RegistrantList(); private final RegistrantList mVideoCapabilityChangedRegistrants = new RegistrantList(); protected final RegistrantList mEmergencyCallToggledRegistrants = new RegistrantList(); private final RegistrantList mCellInfoRegistrants = new RegistrantList(); private final RegistrantList mRedialRegistrants = new RegistrantList(); private final RegistrantList mPhysicalChannelConfigRegistrants = new RegistrantList(); private final RegistrantList mOtaspRegistrants = new RegistrantList(); private final RegistrantList mPreferredNetworkTypeRegistrants = new RegistrantList(); protected Registrant mPostDialHandler; protected final LocalLog mLocalLog; private Looper mLooper; /* to insure registrants are in correct thread*/ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected final Context mContext; protected SubscriptionManagerService mSubscriptionManagerService; /** * PhoneNotifier is an abstraction for all system-wide * state change notification. DefaultPhoneNotifier is * used here unless running we're inside a unit test. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) protected PhoneNotifier mNotifier; protected SimulatedRadioControl mSimulatedRadioControl; private Map mAllowedNetworkTypesForReasons = new HashMap<>(); private static final String ALLOWED_NETWORK_TYPES_TEXT_USER = "user"; private static final String ALLOWED_NETWORK_TYPES_TEXT_POWER = "power"; private static final String ALLOWED_NETWORK_TYPES_TEXT_CARRIER = "carrier"; private static final String ALLOWED_NETWORK_TYPES_TEXT_ENABLE_2G = "enable_2g"; private static final int INVALID_ALLOWED_NETWORK_TYPES = -1; protected boolean mIsCarrierNrSupported = false; protected boolean mIsAllowedNetworkTypesLoadedFromDb = false; private boolean mUnitTestMode; protected VoiceCallSessionStats mVoiceCallSessionStats; protected SmsStats mSmsStats; protected TelephonyAnalytics mTelephonyAnalytics; protected LinkBandwidthEstimator mLinkBandwidthEstimator; public static final int IMEI_TYPE_UNKNOWN = -1; public static final int IMEI_TYPE_PRIMARY = ImeiInfo.ImeiType.PRIMARY; public static final int IMEI_TYPE_SECONDARY = ImeiInfo.ImeiType.SECONDARY; public IccRecords getIccRecords() { return mIccRecords.get(); } /** * Returns a string identifier for this phone interface for parties * outside the phone app process. * @return The string name. */ @UnsupportedAppUsage public String getPhoneName() { return mName; } protected void setPhoneName(String name) { mName = name; } /** * Retrieves Nai for phones. Returns null if Nai is not set. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public String getNai(){ return null; } /** * Return the ActionDetached string. When this action is received by components * they are to simulate detaching from the network. * * @return com.android.internal.telephony.{mName}.action_detached * {mName} is GSM, CDMA ... */ public String getActionDetached() { return mActionDetached; } /** * Return the ActionAttached string. When this action is received by components * they are to simulate attaching to the network. * * @return com.android.internal.telephony.{mName}.action_detached * {mName} is GSM, CDMA ... */ public String getActionAttached() { return mActionAttached; } /** * Set a system property, unless we're in unit test mode */ // CAF_MSIM TODO this need to be replated with TelephonyManager API ? @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public String getSystemProperty(String property, String defValue) { if(getUnitTestMode()) { return null; } return SystemProperties.get(property, defValue); } /** * Constructs a Phone in normal (non-unit test) mode. * * @param name a name for this phone object * @param notifier An instance of DefaultPhoneNotifier, * @param context Context object from hosting application * unless unit testing. * @param ci is CommandsInterface * @param unitTestMode when true, prevents notifications * of state change events * @param featureFlags an instance of the FeatureFlags set */ protected Phone(String name, PhoneNotifier notifier, Context context, CommandsInterface ci, boolean unitTestMode, FeatureFlags featureFlags) { this(name, notifier, context, ci, unitTestMode, SubscriptionManager.DEFAULT_PHONE_INDEX, TelephonyComponentFactory.getInstance(), featureFlags); } /** * Constructs a Phone in normal (non-unit test) mode. * * @param name a name for this phone object * @param notifier An instance of DefaultPhoneNotifier, * @param context Context object from hosting application * unless unit testing. * @param ci is CommandsInterface * @param unitTestMode when true, prevents notifications * of state change events * @param phoneId the phone-id of this phone. * @param telephonyComponentFactory a factory for injecting telephony components * @param featureFlags an instance of the FeatureFlags set */ protected Phone(String name, PhoneNotifier notifier, Context context, CommandsInterface ci, boolean unitTestMode, int phoneId, TelephonyComponentFactory telephonyComponentFactory, FeatureFlags featureFlags) { mPhoneId = phoneId; mName = name; mNotifier = notifier; mContext = context; mLooper = Looper.myLooper(); mCi = ci; mActionDetached = this.getClass().getPackage().getName() + ".action_detached"; mActionAttached = this.getClass().getPackage().getName() + ".action_attached"; mAppSmsManager = telephonyComponentFactory.inject(AppSmsManager.class.getName()) .makeAppSmsManager(context); mLocalLog = new LocalLog(64); mFeatureFlags = featureFlags; setUnitTestMode(unitTestMode); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); mDnsCheckDisabled = sp.getBoolean(DNS_SERVER_CHECK_DISABLED_KEY, false); mCi.setOnCallRing(this, EVENT_CALL_RING, null); /* "Voice capable" means that this device supports circuit-switched * (i.e. voice) phone calls over the telephony network, and is allowed * to display the in-call UI while a cellular voice call is active. * This will be false on "data only" devices which can't make voice * calls and don't support any in-call UI. */ mIsVoiceCapable = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)) .isVoiceCapable(); /** * Some RIL's don't always send RIL_UNSOL_CALL_RING so it needs * to be generated locally. Ideally all ring tones should be loops * and this wouldn't be necessary. But to minimize changes to upper * layers it is requested that it be generated by lower layers. * * By default old phones won't have the property set but do generate * the RIL_UNSOL_CALL_RING so the default if there is no property is * true. */ mDoesRilSendMultipleCallRing = TelephonyProperties.ril_sends_multiple_call_ring() .orElse(true); Rlog.d(LOG_TAG, "mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing); mCallRingDelay = TelephonyProperties.call_ring_delay().orElse(3000); Rlog.d(LOG_TAG, "mCallRingDelay=" + mCallRingDelay); // Initialize SMS stats mSmsStats = new SmsStats(this); mSubscriptionManagerService = SubscriptionManagerService.getInstance(); m2gAdminUpdater = new TelephonyAdminReceiver(context, this); if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) { return; } if (TelephonyUtils.IS_DEBUGGABLE) { mTelephonyTester = new TelephonyTester(this); } // Initialize device storage and outgoing SMS usage monitors for SMSDispatchers. mTelephonyComponentFactory = telephonyComponentFactory; mSmsStorageMonitor = mTelephonyComponentFactory.inject(SmsStorageMonitor.class.getName()) .makeSmsStorageMonitor(this); mSmsUsageMonitor = mTelephonyComponentFactory.inject(SmsUsageMonitor.class.getName()) .makeSmsUsageMonitor(context); mUiccController = UiccController.getInstance(); mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null); mSimActivationTracker = mTelephonyComponentFactory .inject(SimActivationTracker.class.getName()) .makeSimActivationTracker(this); if (getPhoneType() != PhoneConstants.PHONE_TYPE_SIP) { mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null); } //Initialize Telephony Analytics if (mFeatureFlags.enableTelephonyAnalytics()) { mTelephonyAnalytics = new TelephonyAnalytics(this); } } /** * Start setup of ImsPhone, which will start trying to connect to the ImsResolver. Will not be * called if this device does not support FEATURE_IMS_TELEPHONY. */ public void createImsPhone() { if (getPhoneType() == PhoneConstants.PHONE_TYPE_SIP) { return; } synchronized(Phone.lockForRadioTechnologyChange) { if (mImsPhone == null) { mImsPhone = PhoneFactory.makeImsPhone(mNotifier, this); CallManager.getInstance().registerPhone(mImsPhone); mImsPhone.registerForSilentRedial( this, EVENT_INITIATE_SILENT_REDIAL, null); } } } /** * Checks if device should convert CDMA Caller ID restriction related MMI codes to * equivalent 3GPP MMI Codes that provide same functionality when device is roaming. * This method should only return true on multi-mode devices when carrier requires this * conversion to be done on the device. * * @return true when carrier config * "KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL" is set to true */ public boolean supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming() { CarrierConfigManager configManager = (CarrierConfigManager) getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); PersistableBundle b = configManager.getConfigForSubId(getSubId()); if (b != null) { return b.getBoolean( CarrierConfigManager .KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL, false); } else { // Default value set in CarrierConfigManager return false; } } /** * Check if sending CLIR activation("*31#") and deactivation("#31#") code only without dialing * number is prevented. * * @return {@code true} when carrier config * "KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL" is set to {@code true} */ public boolean isClirActivationAndDeactivationPrevented() { CarrierConfigManager configManager = (CarrierConfigManager) getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); PersistableBundle b = configManager.getConfigForSubId(getSubId()); if (b == null) { b = CarrierConfigManager.getDefaultConfig(); } return b.getBoolean( CarrierConfigManager.KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL); } /** * Determines if the carrier prefers to use an in service sim for a normal routed emergency * call. * @return true when carrier config * {@link CarrierConfigManager#KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL} * is true. */ public boolean shouldPreferInServiceSimForNormalRoutedEmergencyCall() { CarrierConfigManager configManager = (CarrierConfigManager) getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); PersistableBundle b = configManager.getConfigForSubId(getSubId(), CarrierConfigManager .KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL); if (b != null) { return b.getBoolean(CarrierConfigManager .KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL, false); } else { // Default value set in CarrierConfigManager return false; } } /** * When overridden the derived class needs to call * super.handleMessage(msg) so this method has a * a chance to process the message. * * @param msg */ @Override public void handleMessage(Message msg) { AsyncResult ar; // messages to be handled whether or not the phone is being destroyed // should only include messages which are being re-directed and do not use // resources of the phone being destroyed switch (msg.what) { // handle the select network completion callbacks. case EVENT_SET_NETWORK_MANUAL_COMPLETE: case EVENT_SET_NETWORK_AUTOMATIC_COMPLETE: handleSetSelectNetwork((AsyncResult) msg.obj); onSetNetworkSelectionModeCompleted(); return; } switch(msg.what) { case EVENT_CALL_RING: Rlog.d(LOG_TAG, "Event EVENT_CALL_RING Received state=" + getState()); ar = (AsyncResult)msg.obj; if (ar.exception == null) { PhoneConstants.State state = getState(); if ((!mDoesRilSendMultipleCallRing) && ((state == PhoneConstants.State.RINGING) || (state == PhoneConstants.State.IDLE))) { mCallRingContinueToken += 1; sendIncomingCallRingNotification(mCallRingContinueToken); } else { notifyIncomingRing(); } } break; case EVENT_CALL_RING_CONTINUE: Rlog.d(LOG_TAG, "Event EVENT_CALL_RING_CONTINUE Received state=" + getState()); if (getState() == PhoneConstants.State.RINGING) { sendIncomingCallRingNotification(msg.arg1); } break; case EVENT_ICC_CHANGED: onUpdateIccAvailability(); break; case EVENT_INITIATE_SILENT_REDIAL: // This is an ImsPhone -> GsmCdmaPhone redial // See ImsPhone#initiateSilentRedial Rlog.d(LOG_TAG, "Event EVENT_INITIATE_SILENT_REDIAL Received"); ar = (AsyncResult) msg.obj; if ((ar.exception == null) && (ar.result != null)) { SilentRedialParam result = (SilentRedialParam) ar.result; String dialString = result.dialString; int causeCode = result.causeCode; DialArgs dialArgs = result.dialArgs; if (TextUtils.isEmpty(dialString)) return; try { Connection cn = dialInternal(dialString, dialArgs); // The ImsPhoneConnection that is owned by the ImsPhone is currently the // one with a callback registered to TelephonyConnection. Notify the // redial happened over that Phone so that it can be replaced with the // new GSM/CDMA Connection. Rlog.d(LOG_TAG, "Notify redial connection changed cn: " + cn); if (mImsPhone != null) { // Don't care it is null or not. mImsPhone.notifyRedialConnectionChanged(cn); } } catch (CallStateException e) { Rlog.e(LOG_TAG, "silent redial failed: " + e); if (mImsPhone != null) { mImsPhone.notifyRedialConnectionChanged(null); } } } break; case EVENT_SRVCC_STATE_CHANGED: ar = (AsyncResult)msg.obj; if (ar.exception == null) { handleSrvccStateChanged((int[]) ar.result); } else { Rlog.e(LOG_TAG, "Srvcc exception: " + ar.exception); } break; case EVENT_UNSOL_OEM_HOOK_RAW: // deprecated, ignore break; case EVENT_CHECK_FOR_NETWORK_AUTOMATIC: { onCheckForNetworkSelectionModeAutomatic(msg); break; } case EVENT_GET_USAGE_SETTING_DONE: ar = (AsyncResult) msg.obj; if (ar.exception == null) { try { mUsageSettingFromModem = ((int[]) ar.result)[0]; } catch (NullPointerException | ClassCastException e) { Rlog.e(LOG_TAG, "Invalid response for usage setting " + ar.result); break; } logd("Received mUsageSettingFromModem=" + mUsageSettingFromModem); if (mUsageSettingFromModem != mPreferredUsageSetting) { mCi.setUsageSetting(obtainMessage(EVENT_SET_USAGE_SETTING_DONE), mPreferredUsageSetting); } } else { try { CommandException ce = (CommandException) ar.exception; if (ce.getCommandError() == CommandException.Error.REQUEST_NOT_SUPPORTED) { mIsUsageSettingSupported = false; } Rlog.w(LOG_TAG, "Unexpected failure to retrieve usage setting " + ce); } catch (ClassCastException unused) { Rlog.e(LOG_TAG, "Invalid Exception for usage setting " + ar.exception); break; // technically extraneous, but good hygiene } } break; case EVENT_SET_USAGE_SETTING_DONE: ar = (AsyncResult) msg.obj; if (ar.exception != null) { try { CommandException ce = (CommandException) ar.exception; if (ce.getCommandError() == CommandException.Error.REQUEST_NOT_SUPPORTED) { mIsUsageSettingSupported = false; } Rlog.w(LOG_TAG, "Unexpected failure to set usage setting " + ce); } catch (ClassCastException unused) { Rlog.e(LOG_TAG, "Invalid Exception for usage setting " + ar.exception); break; // technically extraneous, but good hygiene } } break; default: throw new RuntimeException("unexpected event not handled, msgId=" + msg.what); } } public ArrayList getHandoverConnection() { return null; } /** * Notifies the change of the SRVCC state. * @param state the new SRVCC state. */ public void notifySrvccState(@SrvccState int state) { } public void registerForSilentRedial(Handler h, int what, Object obj) { } public void unregisterForSilentRedial(Handler h) { } public void registerForVolteSilentRedial(Handler h, int what, Object obj) { } public void unregisterForVolteSilentRedial(Handler h) { } private void handleSrvccStateChanged(int[] ret) { Rlog.d(LOG_TAG, "handleSrvccStateChanged"); ArrayList conn = null; Phone imsPhone = mImsPhone; Call.SrvccState srvccState = Call.SrvccState.NONE; if (ret != null && ret.length != 0) { int state = ret[0]; if (imsPhone != null) { imsPhone.notifySrvccState(state); } switch(state) { case TelephonyManager.SRVCC_STATE_HANDOVER_STARTED: srvccState = Call.SrvccState.STARTED; if (imsPhone != null) { conn = imsPhone.getHandoverConnection(); migrateFrom(imsPhone); } else { Rlog.d(LOG_TAG, "HANDOVER_STARTED: mImsPhone null"); } break; case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED: srvccState = Call.SrvccState.COMPLETED; break; case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED: case TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED: srvccState = Call.SrvccState.FAILED; break; default: //ignore invalid state return; } getCallTracker().notifySrvccState(srvccState, conn); notifySrvccStateChanged(state); } } /** * Gets the context for the phone, as set at initialization time. */ @UnsupportedAppUsage public Context getContext() { return mContext; } // Will be called when icc changed protected abstract void onUpdateIccAvailability(); /** * Disables the DNS check (i.e., allows "0.0.0.0"). * Useful for lab testing environment. * @param b true disables the check, false enables. */ public void disableDnsCheck(boolean b) { mDnsCheckDisabled = b; SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putBoolean(DNS_SERVER_CHECK_DISABLED_KEY, b); editor.apply(); } /** * Returns true if the DNS check is currently disabled. */ public boolean isDnsCheckDisabled() { return mDnsCheckDisabled; } /** * Register for getting notifications for change in the Call State {@link Call.State} * This is called PreciseCallState because the call state is more precise than the * {@link PhoneConstants.State} which can be obtained using the {@link PhoneStateListener} * * Resulting events will have an AsyncResult in Message.obj. * AsyncResult.userData will be set to the obj argument here. * The h parameter is held only by a weak reference. */ @UnsupportedAppUsage public void registerForPreciseCallStateChanged(Handler h, int what, Object obj) { checkCorrectThread(h); mPreciseCallStateRegistrants.addUnique(h, what, obj); } /** * Unregisters for voice call state change notifications. * Extraneous calls are tolerated silently. */ @UnsupportedAppUsage public void unregisterForPreciseCallStateChanged(Handler h) { mPreciseCallStateRegistrants.remove(h); } /** * Notifies when a Handover happens due to SRVCC or Silent Redial */ public void registerForHandoverStateChanged(Handler h, int what, Object obj) { checkCorrectThread(h); mHandoverRegistrants.addUnique(h, what, obj); } /** * Unregisters for handover state notifications */ public void unregisterForHandoverStateChanged(Handler h) { mHandoverRegistrants.remove(h); } /** * Subclasses of Phone probably want to replace this with a * version scoped to their packages */ public void notifyHandoverStateChanged(Connection cn) { AsyncResult ar = new AsyncResult(null, cn, null); mHandoverRegistrants.notifyRegistrants(ar); } /** * Notifies when a Handover happens due to Silent Redial */ public void registerForRedialConnectionChanged(Handler h, int what, Object obj) { checkCorrectThread(h); mRedialRegistrants.addUnique(h, what, obj); } /** * Unregisters for redial connection notifications */ public void unregisterForRedialConnectionChanged(Handler h) { mRedialRegistrants.remove(h); } /** * Subclasses of Phone probably want to replace this with a * version scoped to their packages */ public void notifyRedialConnectionChanged(Connection cn) { AsyncResult ar = new AsyncResult(null, cn, null); mRedialRegistrants.notifyRegistrants(ar); } protected void setIsInEmergencyCall() { } /** * Notify the phone that an SMS has been sent. This will be used determine if the SMS was sent * to an emergency address. * @param destinationAddress the address that the SMS was sent to. */ public void notifySmsSent(String destinationAddress) { TelephonyManager m = (TelephonyManager) getContext().getSystemService( Context.TELEPHONY_SERVICE); if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()) { if (m != null && m.isEmergencyNumber(destinationAddress)) { mLocalLog.log("Emergency SMS detected, recording time."); mTimeLastEmergencySmsSentMs = SystemClock.elapsedRealtime(); } } else { if (mContext.getPackageManager() != null && mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TELEPHONY_CALLING)) { if (m != null && m.isEmergencyNumber(destinationAddress)) { mLocalLog.log("Emergency SMS detected, recording time."); mTimeLastEmergencySmsSentMs = SystemClock.elapsedRealtime(); } } } } /** * Determine if the Phone has recently sent an emergency SMS and is still in the interval of * time defined by a carrier that we may need to do perform special actions, for example * override user setting for location so the carrier can find the user's location for emergency * services. * * @return true if the device is in emergency SMS mode, false otherwise. */ public boolean isInEmergencySmsMode() { long lastSmsTimeMs = mTimeLastEmergencySmsSentMs; if (lastSmsTimeMs == EMERGENCY_SMS_NO_TIME_RECORDED) { // an emergency SMS hasn't been sent since the last check. return false; } CarrierConfigManager configManager = (CarrierConfigManager) getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); PersistableBundle b = configManager.getConfigForSubId(getSubId()); if (b == null) { // default for KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT is 0 and CarrierConfig isn't // available, so return false. return false; } int eSmsTimerMs = b.getInt(CarrierConfigManager.KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT, 0); if (eSmsTimerMs == 0) { // We do not support this feature for this carrier. return false; } if (eSmsTimerMs > EMERGENCY_SMS_TIMER_MAX_MS) { eSmsTimerMs = EMERGENCY_SMS_TIMER_MAX_MS; } boolean isInEmergencySmsMode = SystemClock.elapsedRealtime() <= (lastSmsTimeMs + eSmsTimerMs); if (!isInEmergencySmsMode) { // Shortcut this next time so we do not have to waste time if another emergency SMS // hasn't been sent since the last query. mTimeLastEmergencySmsSentMs = EMERGENCY_SMS_NO_TIME_RECORDED; } else { mLocalLog.log("isInEmergencySmsMode: queried while eSMS mode is active."); } return isInEmergencySmsMode; } protected void migrateFrom(Phone from) { migrate(mHandoverRegistrants, from.mHandoverRegistrants); migrate(mPreciseCallStateRegistrants, from.mPreciseCallStateRegistrants); migrate(mNewRingingConnectionRegistrants, from.mNewRingingConnectionRegistrants); migrate(mIncomingRingRegistrants, from.mIncomingRingRegistrants); migrate(mDisconnectRegistrants, from.mDisconnectRegistrants); migrate(mServiceStateRegistrants, from.mServiceStateRegistrants); migrate(mMmiCompleteRegistrants, from.mMmiCompleteRegistrants); migrate(mMmiRegistrants, from.mMmiRegistrants); migrate(mUnknownConnectionRegistrants, from.mUnknownConnectionRegistrants); migrate(mSuppServiceFailedRegistrants, from.mSuppServiceFailedRegistrants); migrate(mCellInfoRegistrants, from.mCellInfoRegistrants); migrate(mRedialRegistrants, from.mRedialRegistrants); // The emergency state of IMS phone will be cleared in ImsPhone#notifySrvccState after // receive SRVCC completed if (from.isInEmergencyCall()) { setIsInEmergencyCall(); } setEcmCanceledForEmergency(from.isEcmCanceledForEmergency()); } protected void migrate(RegistrantList to, RegistrantList from) { if (from == null) { // May be null in some cases, such as testing. return; } from.removeCleared(); for (int i = 0, n = from.size(); i < n; i++) { Registrant r = (Registrant) from.get(i); Message msg = r.messageForRegistrant(); // Since CallManager has already registered with both CS and IMS phones, // the migrate should happen only for those registrants which are not // registered with CallManager.Hence the below check is needed to add // only those registrants to the registrant list which are not // coming from the CallManager. if (msg != null) { if (msg.obj == CallManager.getInstance().getRegistrantIdentifier()) { continue; } else { to.add((Registrant) from.get(i)); } } else { Rlog.d(LOG_TAG, "msg is null"); } } } /** * Notifies when a previously untracked non-ringing/waiting connection has appeared. * This is likely due to some other entity (eg, SIM card application) initiating a call. */ @UnsupportedAppUsage public void registerForUnknownConnection(Handler h, int what, Object obj) { checkCorrectThread(h); mUnknownConnectionRegistrants.addUnique(h, what, obj); } /** * Unregisters for unknown connection notifications. */ @UnsupportedAppUsage public void unregisterForUnknownConnection(Handler h) { mUnknownConnectionRegistrants.remove(h); } /** * Notifies when a new ringing or waiting connection has appeared.

* * Messages received from this: * Message.obj will be an AsyncResult * AsyncResult.userObj = obj * AsyncResult.result = a Connection.

* Please check Connection.isRinging() to make sure the Connection * has not dropped since this message was posted. * If Connection.isRinging() is true, then * Connection.getCall() == Phone.getRingingCall() */ @UnsupportedAppUsage public void registerForNewRingingConnection( Handler h, int what, Object obj) { checkCorrectThread(h); mNewRingingConnectionRegistrants.addUnique(h, what, obj); } /** * Unregisters for new ringing connection notification. * Extraneous calls are tolerated silently */ @UnsupportedAppUsage public void unregisterForNewRingingConnection(Handler h) { mNewRingingConnectionRegistrants.remove(h); } /** * Notifies when phone's video capabilities changes

* * Messages received from this: * Message.obj will be an AsyncResult * AsyncResult.userObj = obj * AsyncResult.result = true if phone supports video calling

*/ public void registerForVideoCapabilityChanged( Handler h, int what, Object obj) { checkCorrectThread(h); mVideoCapabilityChangedRegistrants.addUnique(h, what, obj); // Notify any registrants of the cached video capability as soon as they register. notifyForVideoCapabilityChanged(mIsVideoCapable); } /** * Unregisters for video capability changed notification. * Extraneous calls are tolerated silently */ public void unregisterForVideoCapabilityChanged(Handler h) { mVideoCapabilityChangedRegistrants.remove(h); } /** * Register for notifications when a sInCall VoicePrivacy is enabled * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){ mCi.registerForInCallVoicePrivacyOn(h, what, obj); } /** * Unegister for notifications when a sInCall VoicePrivacy is enabled * * @param h Handler to be removed from the registrant list. */ public void unregisterForInCallVoicePrivacyOn(Handler h){ mCi.unregisterForInCallVoicePrivacyOn(h); } /** * Register for notifications when a sInCall VoicePrivacy is disabled * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){ mCi.registerForInCallVoicePrivacyOff(h, what, obj); } /** * Unregister for notifications when a sInCall VoicePrivacy is disabled * * @param h Handler to be removed from the registrant list. */ public void unregisterForInCallVoicePrivacyOff(Handler h){ mCi.unregisterForInCallVoicePrivacyOff(h); } /** * Notifies when an incoming call rings.

* * Messages received from this: * Message.obj will be an AsyncResult * AsyncResult.userObj = obj * AsyncResult.result = a Connection.

*/ @UnsupportedAppUsage public void registerForIncomingRing( Handler h, int what, Object obj) { checkCorrectThread(h); mIncomingRingRegistrants.addUnique(h, what, obj); } /** * Unregisters for ring notification. * Extraneous calls are tolerated silently */ @UnsupportedAppUsage public void unregisterForIncomingRing(Handler h) { mIncomingRingRegistrants.remove(h); } /** * Notifies when a voice connection has disconnected, either due to local * or remote hangup or error. * * Messages received from this will have the following members:

*

  • Message.obj will be an AsyncResult
  • *
  • AsyncResult.userObj = obj
  • *
  • AsyncResult.result = a Connection object that is * no longer connected.
*/ @UnsupportedAppUsage public void registerForDisconnect(Handler h, int what, Object obj) { checkCorrectThread(h); mDisconnectRegistrants.addUnique(h, what, obj); } /** * Unregisters for voice disconnection notification. * Extraneous calls are tolerated silently */ @UnsupportedAppUsage public void unregisterForDisconnect(Handler h) { mDisconnectRegistrants.remove(h); } /** * Register for notifications when a supplementary service attempt fails. * Message.obj will contain an AsyncResult. * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForSuppServiceFailed(Handler h, int what, Object obj) { checkCorrectThread(h); mSuppServiceFailedRegistrants.addUnique(h, what, obj); } /** * Unregister for notifications when a supplementary service attempt fails. * Extraneous calls are tolerated silently * * @param h Handler to be removed from the registrant list. */ public void unregisterForSuppServiceFailed(Handler h) { mSuppServiceFailedRegistrants.remove(h); } /** * Register for notifications of initiation of a new MMI code request. * MMI codes for GSM are discussed in 3GPP TS 22.030.

* * Example: If Phone.dial is called with "*#31#", then the app will * be notified here.

* * The returned Message.obj will contain an AsyncResult. * * obj.result will be an "MmiCode" object. */ @UnsupportedAppUsage public void registerForMmiInitiate(Handler h, int what, Object obj) { checkCorrectThread(h); mMmiRegistrants.addUnique(h, what, obj); } /** * Unregisters for new MMI initiate notification. * Extraneous calls are tolerated silently */ @UnsupportedAppUsage public void unregisterForMmiInitiate(Handler h) { mMmiRegistrants.remove(h); } /** * Register for notifications that an MMI request has completed * its network activity and is in its final state. This may mean a state * of COMPLETE, FAILED, or CANCELLED. * * Message.obj will contain an AsyncResult. * obj.result will be an "MmiCode" object */ @UnsupportedAppUsage public void registerForMmiComplete(Handler h, int what, Object obj) { checkCorrectThread(h); mMmiCompleteRegistrants.addUnique(h, what, obj); } /** * Unregisters for MMI complete notification. * Extraneous calls are tolerated silently */ @UnsupportedAppUsage public void unregisterForMmiComplete(Handler h) { checkCorrectThread(h); mMmiCompleteRegistrants.remove(h); } /** * Registration point for Sim records loaded * @param h handler to notify * @param what what code of message when delivered * @param obj placed in Message.obj */ @UnsupportedAppUsage public void registerForSimRecordsLoaded(Handler h, int what, Object obj) { } /** * Unregister for notifications for Sim records loaded * @param h Handler to be removed from the registrant list. */ @UnsupportedAppUsage public void unregisterForSimRecordsLoaded(Handler h) { } /** * Register for TTY mode change notifications from the network. * Message.obj will contain an AsyncResult. * AsyncResult.result will be an Integer containing new mode. * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForTtyModeReceived(Handler h, int what, Object obj) { } /** * Unregisters for TTY mode change notifications. * Extraneous calls are tolerated silently * * @param h Handler to be removed from the registrant list. */ public void unregisterForTtyModeReceived(Handler h) { } /** * Switches network selection mode to "automatic", re-scanning and * re-selecting a network if appropriate. * * @param response The message to dispatch when the network selection * is complete. * * @see #selectNetworkManually(OperatorInfo, boolean, android.os.Message) */ @UnsupportedAppUsage public void setNetworkSelectionModeAutomatic(Message response) { Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic, querying current mode"); // we don't want to do this unnecessarily - it actually causes // the radio to repeat network selection and is costly // first check if we're already in automatic mode Message msg = obtainMessage(EVENT_CHECK_FOR_NETWORK_AUTOMATIC); msg.obj = response; mCi.getNetworkSelectionMode(msg); } private void onCheckForNetworkSelectionModeAutomatic(Message fromRil) { AsyncResult ar = (AsyncResult)fromRil.obj; Message response = (Message)ar.userObj; boolean doAutomatic = true; if (ar.exception == null && ar.result != null) { try { int[] modes = (int[])ar.result; if (modes[0] == 0) { // already confirmed to be in automatic mode - don't resend doAutomatic = false; } } catch (Exception e) { // send the setting on error } } // wrap the response message in our own message along with // an empty string (to indicate automatic selection) for the // operator's id. NetworkSelectMessage nsm = new NetworkSelectMessage(); nsm.message = response; nsm.operatorNumeric = ""; nsm.operatorAlphaLong = ""; nsm.operatorAlphaShort = ""; if (doAutomatic) { Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic - set network selection auto"); Message msg = obtainMessage(EVENT_SET_NETWORK_AUTOMATIC_COMPLETE, nsm); mCi.setNetworkSelectionModeAutomatic(msg); } else { Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic - already auto, ignoring"); // let the calling application know that the we are ignoring automatic mode switch. if (nsm.message != null) { nsm.message.arg1 = ALREADY_IN_AUTO_SELECTION; } ar.userObj = nsm; handleSetSelectNetwork(ar); } updateSavedNetworkOperator(nsm); } /** * Called when setting network selection mode is complete. */ protected void onSetNetworkSelectionModeCompleted() { } /** * Query the radio for the current network selection mode. * * Return values: * 0 - automatic. * 1 - manual. */ public void getNetworkSelectionMode(Message message) { mCi.getNetworkSelectionMode(message); } public List getClientRequestStats() { return mCi.getClientRequestStats(); } /** * Manually selects a network. response is * dispatched when this is complete. response.obj will be * an AsyncResult, and response.obj.exception will be non-null * on failure. * * @see #setNetworkSelectionModeAutomatic(Message) */ @UnsupportedAppUsage public void selectNetworkManually(OperatorInfo network, boolean persistSelection, Message response) { // wrap the response message in our own message along with // the operator's id. NetworkSelectMessage nsm = new NetworkSelectMessage(); nsm.message = response; nsm.operatorNumeric = network.getOperatorNumeric(); nsm.operatorAlphaLong = network.getOperatorAlphaLong(); nsm.operatorAlphaShort = network.getOperatorAlphaShort(); Message msg = obtainMessage(EVENT_SET_NETWORK_MANUAL_COMPLETE, nsm); mCi.setNetworkSelectionModeManual(network.getOperatorNumeric(), network.getRan(), msg); if (persistSelection) { updateSavedNetworkOperator(nsm); } else { clearSavedNetworkSelection(); updateManualNetworkSelection(nsm); } } /** * Registration point for emergency call/callback mode start. Message.obj is AsyncResult and * Message.obj.result will be Integer indicating start of call by value 1 or end of call by * value 0 * @param h handler to notify * @param what what code of message when delivered * @param obj placed in Message.obj.userObj */ public void registerForEmergencyCallToggle(Handler h, int what, Object obj) { Registrant r = new Registrant(h, what, obj); mEmergencyCallToggledRegistrants.add(r); } public void unregisterForEmergencyCallToggle(Handler h) { mEmergencyCallToggledRegistrants.remove(h); } private void updateSavedNetworkOperator(NetworkSelectMessage nsm) { int subId = getSubId(); if (SubscriptionManager.isValidSubscriptionId(subId)) { // open the shared preferences editor, and write the value. // nsm.operatorNumeric is "" if we're in automatic.selection. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putString(NETWORK_SELECTION_KEY + subId, nsm.operatorNumeric); editor.putString(NETWORK_SELECTION_NAME_KEY + subId, nsm.operatorAlphaLong); editor.putString(NETWORK_SELECTION_SHORT_KEY + subId, nsm.operatorAlphaShort); // commit and log the result. if (!editor.commit()) { Rlog.e(LOG_TAG, "failed to commit network selection preference"); } } else { Rlog.e(LOG_TAG, "Cannot update network selection preference due to invalid subId " + subId); } } /** * Update non-perisited manual network selection. * * @param nsm PLMN info of the selected network */ protected void updateManualNetworkSelection(NetworkSelectMessage nsm) { Rlog.e(LOG_TAG, "updateManualNetworkSelection() should be overridden"); } /** * Used to track the settings upon completion of the network change. */ private void handleSetSelectNetwork(AsyncResult ar) { // look for our wrapper within the asyncresult, skip the rest if it // is null. if (!(ar.userObj instanceof NetworkSelectMessage)) { Rlog.e(LOG_TAG, "unexpected result from user object."); return; } NetworkSelectMessage nsm = (NetworkSelectMessage) ar.userObj; // found the object, now we send off the message we had originally // attached to the request. if (nsm.message != null) { AsyncResult.forMessage(nsm.message, ar.result, ar.exception); nsm.message.sendToTarget(); } } /** * Method to retrieve the saved operator from the Shared Preferences */ @NonNull public OperatorInfo getSavedNetworkSelection() { // open the shared preferences and search with our key. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); String numeric = sp.getString(NETWORK_SELECTION_KEY + getSubId(), ""); String name = sp.getString(NETWORK_SELECTION_NAME_KEY + getSubId(), ""); String shrt = sp.getString(NETWORK_SELECTION_SHORT_KEY + getSubId(), ""); return new OperatorInfo(name, shrt, numeric); } /** * Clears the saved network selection. */ private void clearSavedNetworkSelection() { // open the shared preferences and search with our key. PreferenceManager.getDefaultSharedPreferences(getContext()).edit(). remove(NETWORK_SELECTION_KEY + getSubId()). remove(NETWORK_SELECTION_NAME_KEY + getSubId()). remove(NETWORK_SELECTION_SHORT_KEY + getSubId()).commit(); } /** * Method to restore the previously saved operator id, or reset to * automatic selection, all depending upon the value in the shared * preferences. */ private void restoreSavedNetworkSelection(Message response) { // retrieve the operator OperatorInfo networkSelection = getSavedNetworkSelection(); // set to auto if the id is empty, otherwise select the network. if (networkSelection == null || TextUtils.isEmpty(networkSelection.getOperatorNumeric())) { setNetworkSelectionModeAutomatic(response); } else { selectNetworkManually(networkSelection, true, response); } } /** * Saves CLIR setting so that we can re-apply it as necessary * (in case the RIL resets it across reboots). */ public void saveClirSetting(int commandInterfaceCLIRMode) { // Open the shared preferences editor, and write the value. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putInt(CLIR_KEY + getSubId(), commandInterfaceCLIRMode); Rlog.i(LOG_TAG, "saveClirSetting: " + CLIR_KEY + getSubId() + "=" + commandInterfaceCLIRMode); // Commit and log the result. if (!editor.commit()) { Rlog.e(LOG_TAG, "Failed to commit CLIR preference"); } } /** * For unit tests; don't send notifications to "Phone" * mailbox registrants if true. */ private void setUnitTestMode(boolean f) { mUnitTestMode = f; } /** * @return true If unit test mode is enabled */ public boolean getUnitTestMode() { return mUnitTestMode; } /** * To be invoked when a voice call Connection disconnects. * * Subclasses of Phone probably want to replace this with a * version scoped to their packages */ protected void notifyDisconnectP(Connection cn) { AsyncResult ar = new AsyncResult(null, cn, null); mDisconnectRegistrants.notifyRegistrants(ar); } /** * Register for ServiceState changed. * Message.obj will contain an AsyncResult. * AsyncResult.result will be a ServiceState instance */ @UnsupportedAppUsage public void registerForServiceStateChanged( Handler h, int what, Object obj) { mServiceStateRegistrants.add(h, what, obj); } /** * Unregisters for ServiceStateChange notification. * Extraneous calls are tolerated silently */ @UnsupportedAppUsage public void unregisterForServiceStateChanged(Handler h) { mServiceStateRegistrants.remove(h); } /** * Notifies when out-band ringback tone is needed.

* * Messages received from this: * Message.obj will be an AsyncResult * AsyncResult.userObj = obj * AsyncResult.result = boolean, true to start play ringback tone * and false to stop.

*/ @UnsupportedAppUsage public void registerForRingbackTone(Handler h, int what, Object obj) { mCi.registerForRingbackTone(h, what, obj); } /** * Unregisters for ringback tone notification. */ @UnsupportedAppUsage public void unregisterForRingbackTone(Handler h) { mCi.unregisterForRingbackTone(h); } /** * Notifies when out-band on-hold tone is needed.

* * Messages received from this: * Message.obj will be an AsyncResult * AsyncResult.userObj = obj * AsyncResult.result = boolean, true to start play on-hold tone * and false to stop.

*/ public void registerForOnHoldTone(Handler h, int what, Object obj) { } /** * Unregisters for on-hold tone notification. */ public void unregisterForOnHoldTone(Handler h) { } /** * Registers the handler to reset the uplink mute state to get * uplink audio. */ public void registerForResendIncallMute(Handler h, int what, Object obj) { mCi.registerForResendIncallMute(h, what, obj); } /** * Unregisters for resend incall mute notifications. */ public void unregisterForResendIncallMute(Handler h) { mCi.unregisterForResendIncallMute(h); } /** * Registers for CellInfo changed. * Message.obj will contain an AsyncResult. * AsyncResult.result will be a List instance */ public void registerForCellInfo( Handler h, int what, Object obj) { mCellInfoRegistrants.add(h, what, obj); } /** * Unregisters for CellInfo notification. * Extraneous calls are tolerated silently */ public void unregisterForCellInfo(Handler h) { mCellInfoRegistrants.remove(h); } /** * Enables or disables echo suppression. */ public void setEchoSuppressionEnabled() { // no need for regular phone } /** * Subclasses of Phone probably want to replace this with a * version scoped to their packages */ protected void notifyServiceStateChangedP(ServiceState ss) { AsyncResult ar = new AsyncResult(null, new ServiceState(ss), null); mServiceStateRegistrants.notifyRegistrants(ar); mNotifier.notifyServiceState(this); } /** * Version of notifyServiceStateChangedP which allows us to specify the subId. This is used when * we send out a final ServiceState update when a phone's subId becomes invalid. */ protected void notifyServiceStateChangedPForSubId(ServiceState ss, int subId) { AsyncResult ar = new AsyncResult(null, ss, null); mServiceStateRegistrants.notifyRegistrants(ar); mNotifier.notifyServiceStateForSubId(this, ss, subId); } /** * If this is a simulated phone interface, returns a SimulatedRadioControl. * @return SimulatedRadioControl if this is a simulated interface; * otherwise, null. */ public SimulatedRadioControl getSimulatedRadioControl() { return mSimulatedRadioControl; } /** * Verifies the current thread is the same as the thread originally * used in the initialization of this instance. Throws RuntimeException * if not. * * @exception RuntimeException if the current thread is not * the thread that originally obtained this Phone instance. */ private void checkCorrectThread(Handler h) { if (h.getLooper() != mLooper) { throw new RuntimeException( "com.android.internal.telephony.Phone must be used from within one thread"); } } /** * Set the properties by matching the carrier string in * a string-array resource */ @Nullable Locale getLocaleFromCarrierProperties() { String carrier = SystemProperties.get("ro.carrier"); if (null == carrier || 0 == carrier.length() || "unknown".equals(carrier)) { return null; } CharSequence[] carrierLocales = mContext.getResources().getTextArray( R.array.carrier_properties); for (int i = 0; i < carrierLocales.length; i+=3) { String c = carrierLocales[i].toString(); if (carrier.equals(c)) { return Locale.forLanguageTag(carrierLocales[i + 1].toString().replace('_', '-')); } } return null; } /** * Get current coarse-grained voice call state. * Use {@link #registerForPreciseCallStateChanged(Handler, int, Object) * registerForPreciseCallStateChanged()} for change notification.

* If the phone has an active call and call waiting occurs, * then the phone state is RINGING not OFFHOOK * Note: * This registration point provides notification of finer-grained * changes.

*/ @UnsupportedAppUsage public abstract PhoneConstants.State getState(); /** * Retrieves the IccFileHandler of the Phone instance */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public IccFileHandler getIccFileHandler(){ UiccCardApplication uiccApplication = mUiccApplication.get(); IccFileHandler fh; if (uiccApplication == null) { Rlog.d(LOG_TAG, "getIccFileHandler: uiccApplication == null, return null"); fh = null; } else { fh = uiccApplication.getIccFileHandler(); } Rlog.d(LOG_TAG, "getIccFileHandler: fh=" + fh); return fh; } /* * Retrieves the Handler of the Phone instance */ public Handler getHandler() { return this; } /** * Update the phone object if the voice radio technology has changed * * @param voiceRadioTech The new voice radio technology */ public void updatePhoneObject(int voiceRadioTech) { } /** * Retrieves the ServiceStateTracker of the phone instance. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public ServiceStateTracker getServiceStateTracker() { return null; } /** * Override to merge into {@link #getServiceState} when telecom has registered a SIM call * manager that supports over-the-top SIM-based calling (e.g. carrier-provided wi-fi calling * implementation). * * @param hasService Whether or not the SIM call manager currently provides over-the-top voice */ public void setVoiceServiceStateOverride(boolean hasService) {} /** * Check whether the radio is off for thermal reason. * * @return {@code true} only if thermal mitigation is one of the reason for which radio is off. */ public boolean isRadioOffForThermalMitigation() { ServiceStateTracker sst = getServiceStateTracker(); return sst != null && sst.getRadioPowerOffReasons() .contains(TelephonyManager.RADIO_POWER_REASON_THERMAL); } /** * Retrieves the EmergencyNumberTracker of the phone instance. */ public EmergencyNumberTracker getEmergencyNumberTracker() { return null; } /** * Get call tracker */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public CallTracker getCallTracker() { return null; } /** * @return The instance of access networks manager. */ public AccessNetworksManager getAccessNetworksManager() { return null; } /** * Retrieves the DeviceStateMonitor of the phone instance. */ public DeviceStateMonitor getDeviceStateMonitor() { return null; } /** * Retrieves the DisplayInfoController of the phone instance. */ public DisplayInfoController getDisplayInfoController() { return null; } /** * Retrieves the SignalStrengthController of the phone instance. */ public SignalStrengthController getSignalStrengthController() { Log.wtf(LOG_TAG, "getSignalStrengthController return null."); return null; } /** * Update voice activation state */ public void setVoiceActivationState(int state) { mSimActivationTracker.setVoiceActivationState(state); } /** * Update data activation state */ public void setDataActivationState(int state) { mSimActivationTracker.setDataActivationState(state); } /** * Returns voice activation state */ public int getVoiceActivationState() { return mSimActivationTracker.getVoiceActivationState(); } /** * Returns data activation state */ public int getDataActivationState() { return mSimActivationTracker.getDataActivationState(); } /** * Update voice mail count related fields and notify listeners */ public void updateVoiceMail() { Rlog.e(LOG_TAG, "updateVoiceMail() should be overridden"); } public AppType getCurrentUiccAppType() { UiccCardApplication currentApp = mUiccApplication.get(); if (currentApp != null) { return currentApp.getType(); } return AppType.APPTYPE_UNKNOWN; } /** * Returns the ICC card interface for this phone, or null * if not applicable to underlying technology. */ @UnsupportedAppUsage public IccCard getIccCard() { return null; //throw new Exception("getIccCard Shouldn't be called from Phone"); } /** * Retrieves the serial number of the ICC, if applicable. Returns only the decimal digits before * the first hex digit in the ICC ID. */ @UnsupportedAppUsage public String getIccSerialNumber() { IccRecords r = mIccRecords.get(); return (r != null) ? r.getIccId() : null; } /** * Retrieves the full serial number of the ICC (including hex digits), if applicable. */ public String getFullIccSerialNumber() { IccRecords r = mIccRecords.get(); return (r != null) ? r.getFullIccId() : null; } /** * Returns SIM record load state. Use * getSimCard().registerForReady() for change notification. * * @return true if records from the SIM have been loaded and are * available (if applicable). If not applicable to the underlying * technology, returns true as well. */ public boolean getIccRecordsLoaded() { IccRecords r = mIccRecords.get(); return (r != null) ? r.getRecordsLoaded() : false; } /** Set the minimum interval for CellInfo requests to the modem */ public void setCellInfoMinInterval(int interval) { getServiceStateTracker().setCellInfoMinInterval(interval); } /** * @return the last known CellInfo */ public List getAllCellInfo() { return getServiceStateTracker().getAllCellInfo(); } /** * @param workSource calling WorkSource * @param rspMsg the response message containing the cell info */ public void requestCellInfoUpdate(WorkSource workSource, Message rspMsg) { getServiceStateTracker().requestAllCellInfo(workSource, rspMsg); } /** * Returns the current CellIdentity if known */ public CellIdentity getCurrentCellIdentity() { return getServiceStateTracker().getCellIdentity(); } /** * @param workSource calling WorkSource * @param rspMsg the response message containing the cell location */ public void getCellIdentity(WorkSource workSource, Message rspMsg) { getServiceStateTracker().requestCellIdentity(workSource, rspMsg); } /** * Sets the minimum time in milli-seconds between {@link PhoneStateListener#onCellInfoChanged * PhoneStateListener.onCellInfoChanged} will be invoked. * * The default, 0, means invoke onCellInfoChanged when any of the reported * information changes. Setting the value to INT_MAX(0x7fffffff) means never issue * A onCellInfoChanged. * * @param rateInMillis the rate * @param workSource calling WorkSource */ public void setCellInfoListRate(int rateInMillis, WorkSource workSource) { mCi.setCellInfoListRate(rateInMillis, null, workSource); } /** * Get voice message waiting indicator status. No change notification * available on this interface. Use PhoneStateNotifier or similar instead. * * @return true if there is a voice message waiting */ public boolean getMessageWaitingIndicator() { return mVmCount != 0; } /** * Retrieves manually selected network info. */ public String getManualNetworkSelectionPlmn() { return null; } private int getCallForwardingIndicatorFromSharedPref() { int status = IccRecords.CALL_FORWARDING_STATUS_DISABLED; int subId = getSubId(); if (SubscriptionManager.isValidSubscriptionId(subId)) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); status = sp.getInt(CF_STATUS + subId, IccRecords.CALL_FORWARDING_STATUS_UNKNOWN); Rlog.d(LOG_TAG, "getCallForwardingIndicatorFromSharedPref: for subId " + subId + "= " + status); // Check for old preference if status is UNKNOWN for current subId. This part of the // code is needed only when upgrading from M to N. if (status == IccRecords.CALL_FORWARDING_STATUS_UNKNOWN) { String subscriberId = sp.getString(CF_ID, null); if (subscriberId != null) { String currentSubscriberId = getSubscriberId(); if (subscriberId.equals(currentSubscriberId)) { // get call forwarding status from preferences status = sp.getInt(CF_STATUS, IccRecords.CALL_FORWARDING_STATUS_DISABLED); setCallForwardingIndicatorInSharedPref( status == IccRecords.CALL_FORWARDING_STATUS_ENABLED ? true : false); Rlog.d(LOG_TAG, "getCallForwardingIndicatorFromSharedPref: " + status); } else { Rlog.d(LOG_TAG, "getCallForwardingIndicatorFromSharedPref: returning " + "DISABLED as status for matching subscriberId not found"); } // get rid of old preferences. SharedPreferences.Editor editor = sp.edit(); editor.remove(CF_ID); editor.remove(CF_STATUS); editor.apply(); } } } else { Rlog.e(LOG_TAG, "getCallForwardingIndicatorFromSharedPref: invalid subId " + subId); } return status; } private void setCallForwardingIndicatorInSharedPref(boolean enable) { int status = enable ? IccRecords.CALL_FORWARDING_STATUS_ENABLED : IccRecords.CALL_FORWARDING_STATUS_DISABLED; int subId = getSubId(); Rlog.i(LOG_TAG, "setCallForwardingIndicatorInSharedPref: Storing status = " + status + " in pref " + CF_STATUS + subId); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); SharedPreferences.Editor editor = sp.edit(); editor.putInt(CF_STATUS + subId, status); editor.apply(); } public void setVoiceCallForwardingFlag(int line, boolean enable, String number) { setCallForwardingIndicatorInSharedPref(enable); IccRecords r = getIccRecords(); if (r != null) { r.setVoiceCallForwardingFlag(line, enable, number); } notifyCallForwardingIndicator(); } /** * Set the voice call forwarding flag for GSM/UMTS and the like SIMs * * @param r to enable/disable * @param line to enable/disable * @param enable * @param number to which CFU is enabled */ public void setVoiceCallForwardingFlag(IccRecords r, int line, boolean enable, String number) { setCallForwardingIndicatorInSharedPref(enable); if (r != null) { r.setVoiceCallForwardingFlag(line, enable, number); } notifyCallForwardingIndicator(); } /** * Get voice call forwarding indicator status. No change notification * available on this interface. Use PhoneStateNotifier or similar instead. * * @return true if there is a voice call forwarding */ public boolean getCallForwardingIndicator() { if (getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { Rlog.e(LOG_TAG, "getCallForwardingIndicator: not possible in CDMA"); return false; } IccRecords r = getIccRecords(); int callForwardingIndicator = IccRecords.CALL_FORWARDING_STATUS_UNKNOWN; if (r != null) { callForwardingIndicator = r.getVoiceCallForwardingFlag(); } if (callForwardingIndicator == IccRecords.CALL_FORWARDING_STATUS_UNKNOWN) { callForwardingIndicator = getCallForwardingIndicatorFromSharedPref(); } Rlog.v(LOG_TAG, "getCallForwardingIndicator: iccForwardingFlag=" + (r != null ? r.getVoiceCallForwardingFlag() : "null") + ", sharedPrefFlag=" + getCallForwardingIndicatorFromSharedPref()); return (callForwardingIndicator == IccRecords.CALL_FORWARDING_STATUS_ENABLED); } public CarrierSignalAgent getCarrierSignalAgent() { return mCarrierSignalAgent; } public CarrierActionAgent getCarrierActionAgent() { return mCarrierActionAgent; } /** * Query the CDMA roaming preference setting. * * @param response is callback message to report one of TelephonyManager#CDMA_ROAMING_MODE_* */ public void queryCdmaRoamingPreference(Message response) { mCi.queryCdmaRoamingPreference(response); } /** * Get the CDMA subscription mode setting. * * @param response is callback message to report one of TelephonyManager#CDMA_SUBSCRIPTION_* */ public void queryCdmaSubscriptionMode(Message response) { mCi.getCdmaSubscriptionSource(response); } /** * Get current signal strength. No change notification available on this * interface. Use PhoneStateNotifier or an equivalent. * An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu). * The following special values are defined:

*
  • 0 means "-113 dBm or less".
  • *
  • 31 means "-51 dBm or greater".
* * @return Current signal strength as SignalStrength */ public @NonNull SignalStrength getSignalStrength() { SignalStrengthController ssc = getSignalStrengthController(); if (ssc == null) { return new SignalStrength(); } else { return ssc.getSignalStrength(); } } /** * @return true, if the device is in a state where both voice and data * are supported simultaneously. This can change based on location or network condition. */ public boolean isConcurrentVoiceAndDataAllowed() { ServiceStateTracker sst = getServiceStateTracker(); return sst == null ? false : sst.isConcurrentVoiceAndDataAllowed(); } /** * Requests to set the CDMA roaming preference * @param cdmaRoamingType one of TelephonyManager#CDMA_ROAMING_MODE_* * @param response is callback message */ public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) { mCi.setCdmaRoamingPreference(cdmaRoamingType, response); } /** * Requests to set the CDMA subscription mode * @param cdmaSubscriptionType one of TelephonyManager#CDMA_SUBSCRIPTION_* * @param response is callback message */ public void setCdmaSubscriptionMode(int cdmaSubscriptionType, Message response) { mCi.setCdmaSubscriptionSource(cdmaSubscriptionType, response); } /** * Get the effective allowed network types on the device. * * @return effective network type */ private @TelephonyManager.NetworkTypeBitMask long getEffectiveAllowedNetworkTypes() { long allowedNetworkTypes = TelephonyManager.getAllNetworkTypesBitmask(); synchronized (mAllowedNetworkTypesForReasons) { for (long networkTypes : mAllowedNetworkTypesForReasons.values()) { allowedNetworkTypes = allowedNetworkTypes & networkTypes; } } if (!mIsCarrierNrSupported) { allowedNetworkTypes &= ~TelephonyManager.NETWORK_TYPE_BITMASK_NR; } if (m2gAdminUpdater.isCellular2gDisabled()) { logd("SubId " + getSubId() + " disabling 2g in getEffectiveAllowedNetworkTypes according to admin user " + "restriction"); allowedNetworkTypes &= ~TelephonyManager.NETWORK_CLASS_BITMASK_2G; } logd("SubId" + getSubId() + ",getEffectiveAllowedNetworkTypes: " + TelephonyManager.convertNetworkTypeBitmaskToString(allowedNetworkTypes)); return allowedNetworkTypes; } /** * Notify the latest allowed network types changed. */ public void notifyAllowedNetworkTypesChanged( @TelephonyManager.AllowedNetworkTypesReason int reason) { logd("SubId" + getSubId() + ",notifyAllowedNetworkTypesChanged: reason: " + reason + " value:" + TelephonyManager.convertNetworkTypeBitmaskToString( getAllowedNetworkTypes(reason))); mNotifier.notifyAllowedNetworkTypesChanged(this, reason, getAllowedNetworkTypes(reason)); } /** * Is E-UTRA-NR Dual Connectivity enabled */ public void isNrDualConnectivityEnabled(Message message, WorkSource workSource) { mCi.isNrDualConnectivityEnabled(message, workSource); } /** * Enable/Disable E-UTRA-NR Dual Connectivity * @param nrDualConnectivityState expected NR dual connectivity state * This can be passed following states *
    *
  1. Enable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_ENABLE} *
  2. Disable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE} *
  3. Disable NR dual connectivity and force secondary cell to be released * {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE} *
*/ public void setNrDualConnectivityState( @TelephonyManager.NrDualConnectivityState int nrDualConnectivityState, Message message, WorkSource workSource) { mCi.setNrDualConnectivityState(nrDualConnectivityState, message, workSource); } /** * Get the allowed network types for a certain reason. * @param reason reason to configure allowed network types * @return the allowed network types. */ public @TelephonyManager.NetworkTypeBitMask long getAllowedNetworkTypes( @TelephonyManager.AllowedNetworkTypesReason int reason) { long allowedNetworkTypes; long defaultAllowedNetworkTypes = RadioAccessFamily.getRafFromNetworkType( RILConstants.PREFERRED_NETWORK_MODE); if (!TelephonyManager.isValidAllowedNetworkTypesReason(reason)) { throw new IllegalArgumentException("AllowedNetworkTypes NumberFormat exception"); } synchronized (mAllowedNetworkTypesForReasons) { allowedNetworkTypes = mAllowedNetworkTypesForReasons.getOrDefault( reason, defaultAllowedNetworkTypes); } if (!mIsCarrierNrSupported && reason == TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER) { allowedNetworkTypes = updateAllowedNetworkTypeForCarrierWithCarrierConfig(); } logd("SubId" + getSubId() + ",get allowed network types " + convertAllowedNetworkTypeMapIndexToDbName(reason) + ": value = " + TelephonyManager.convertNetworkTypeBitmaskToString( allowedNetworkTypes)); return allowedNetworkTypes; } /** * Loads the allowed network type from subscription database. */ public void loadAllowedNetworksFromSubscriptionDatabase() { String result = null; SubscriptionInfoInternal subInfo = mSubscriptionManagerService .getSubscriptionInfoInternal(getSubId()); if (subInfo != null) { result = subInfo.getAllowedNetworkTypesForReasons(); } // After fw load network type from DB, do unlock if subId is valid. mIsAllowedNetworkTypesLoadedFromDb = SubscriptionManager.isValidSubscriptionId( getSubId()); if (TextUtils.isEmpty(result)) { return; } logd("SubId" + getSubId() + ",load allowed network types : value = " + result); Map oldAllowedNetworkTypes = new HashMap<>(mAllowedNetworkTypesForReasons); mAllowedNetworkTypesForReasons.clear(); try { // Format: "REASON=VALUE,REASON2=VALUE2" for (String pair : result.trim().split(",")) { String[] networkTypesValues = (pair.trim().toLowerCase(Locale.ROOT)).split("="); if (networkTypesValues.length != 2) { Rlog.e(LOG_TAG, "Invalid ALLOWED_NETWORK_TYPES from DB, value = " + pair); continue; } int key = convertAllowedNetworkTypeDbNameToMapIndex(networkTypesValues[0]); long value = Long.parseLong(networkTypesValues[1]); if (TelephonyManager.isValidAllowedNetworkTypesReason(key) && value != INVALID_ALLOWED_NETWORK_TYPES) { synchronized (mAllowedNetworkTypesForReasons) { mAllowedNetworkTypesForReasons.put(key, value); } if (!oldAllowedNetworkTypes.containsKey(key) || oldAllowedNetworkTypes.get(key) != value) { if (oldAllowedNetworkTypes.containsKey(key)) { oldAllowedNetworkTypes.remove(key); } notifyAllowedNetworkTypesChanged(key); } } } } catch (NumberFormatException e) { Rlog.e(LOG_TAG, "allowedNetworkTypes NumberFormat exception" + e); } for (int key : oldAllowedNetworkTypes.keySet()) { notifyAllowedNetworkTypesChanged(key); } } private int convertAllowedNetworkTypeDbNameToMapIndex(String name) { switch (name) { case ALLOWED_NETWORK_TYPES_TEXT_USER: return TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER; case ALLOWED_NETWORK_TYPES_TEXT_POWER: return TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER; case ALLOWED_NETWORK_TYPES_TEXT_CARRIER: return TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER; case ALLOWED_NETWORK_TYPES_TEXT_ENABLE_2G: return TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G; default: return INVALID_ALLOWED_NETWORK_TYPES; } } /** * Convert the allowed network types reason to string. * * @param reason The allowed network types reason. * * @return The converted string. */ public static String convertAllowedNetworkTypeMapIndexToDbName( @TelephonyManager.AllowedNetworkTypesReason int reason) { switch (reason) { case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER: return ALLOWED_NETWORK_TYPES_TEXT_USER; case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER: return ALLOWED_NETWORK_TYPES_TEXT_POWER; case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER: return ALLOWED_NETWORK_TYPES_TEXT_CARRIER; case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G: return ALLOWED_NETWORK_TYPES_TEXT_ENABLE_2G; default: throw new IllegalArgumentException( "No DB name conversion available for allowed network type reason: " + reason + ". Did you forget to add an ALLOWED_NETWORK_TYPE_TEXT entry for" + " a new reason?"); } } private @TelephonyManager.NetworkTypeBitMask long updateAllowedNetworkTypeForCarrierWithCarrierConfig() { long defaultAllowedNetworkTypes = RadioAccessFamily.getRafFromNetworkType( RILConstants.PREFERRED_NETWORK_MODE); long allowedNetworkTypes; synchronized (mAllowedNetworkTypesForReasons) { allowedNetworkTypes = mAllowedNetworkTypesForReasons.getOrDefault( TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER, defaultAllowedNetworkTypes); } if (mIsCarrierNrSupported) { return allowedNetworkTypes; } allowedNetworkTypes = allowedNetworkTypes & ~TelephonyManager.NETWORK_TYPE_BITMASK_NR; logd("Allowed network types for 'carrier' reason is changed by carrier config = " + TelephonyManager.convertNetworkTypeBitmaskToString(allowedNetworkTypes)); return allowedNetworkTypes; } /** * Requests to set the allowed network types for a specific reason * * @param reason reason to configure allowed network type * @param networkTypes one of the network types * @param response callback Message */ public void setAllowedNetworkTypes(@TelephonyManager.AllowedNetworkTypesReason int reason, @TelephonyManager.NetworkTypeBitMask long networkTypes, @Nullable Message response) { int subId = getSubId(); if (!TelephonyManager.isValidAllowedNetworkTypesReason(reason)) { loge("setAllowedNetworkTypes: Invalid allowed network type reason: " + reason); if (response != null) { AsyncResult.forMessage(response, null, new CommandException(CommandException.Error.INVALID_ARGUMENTS)); response.sendToTarget(); } return; } if (!SubscriptionManager.isUsableSubscriptionId(subId) || !mIsAllowedNetworkTypesLoadedFromDb) { loge("setAllowedNetworkTypes: no sim or network type is not loaded. SubscriptionId: " + subId + ", isNetworkTypeLoaded" + mIsAllowedNetworkTypesLoadedFromDb); if (response != null) { AsyncResult.forMessage(response, null, new CommandException(CommandException.Error.MISSING_RESOURCE)); response.sendToTarget(); } return; } String mapAsString = ""; synchronized (mAllowedNetworkTypesForReasons) { mAllowedNetworkTypesForReasons.put(reason, networkTypes); mapAsString = mAllowedNetworkTypesForReasons.keySet().stream() .map(key -> convertAllowedNetworkTypeMapIndexToDbName(key) + "=" + mAllowedNetworkTypesForReasons.get(key)) .collect(Collectors.joining(",")); } SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.ALLOWED_NETWORK_TYPES, mapAsString); logd("setAllowedNetworkTypes: SubId" + subId + ",setAllowedNetworkTypes " + mapAsString); updateAllowedNetworkTypes(response); notifyAllowedNetworkTypesChanged(reason); } protected void updateAllowedNetworkTypes(Message response) { int modemRaf = getRadioAccessFamily(); if (modemRaf == RadioAccessFamily.RAF_UNKNOWN) { Rlog.d(LOG_TAG, "setPreferredNetworkType: Abort, unknown RAF: " + modemRaf); if (response != null) { CommandException ex; ex = new CommandException(CommandException.Error.GENERIC_FAILURE); AsyncResult.forMessage(response, null, ex); response.sendToTarget(); } return; } int filteredRaf = (int) (modemRaf & getEffectiveAllowedNetworkTypes()); logd("setAllowedNetworkTypes: modemRafBitMask = " + modemRaf + " ,modemRaf = " + TelephonyManager.convertNetworkTypeBitmaskToString(modemRaf) + " ,filteredRafBitMask = " + filteredRaf + " ,filteredRaf = " + TelephonyManager.convertNetworkTypeBitmaskToString( filteredRaf)); mCi.setAllowedNetworkTypesBitmap(filteredRaf, response); mPreferredNetworkTypeRegistrants.notifyRegistrants(); } /** * Query the allowed network types bitmask setting * * @param response is callback message to report network types bitmask */ public void getAllowedNetworkTypesBitmask(Message response) { mCi.getAllowedNetworkTypesBitmap(response); } /** * Register for preferred network type changes * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForPreferredNetworkTypeChanged(Handler h, int what, Object obj) { checkCorrectThread(h); mPreferredNetworkTypeRegistrants.addUnique(h, what, obj); } /** * Unregister for preferred network type changes. * * @param h Handler that should be unregistered. */ public void unregisterForPreferredNetworkTypeChanged(Handler h) { mPreferredNetworkTypeRegistrants.remove(h); } /** * Get the cached value of the preferred network type setting */ public int getCachedAllowedNetworkTypesBitmask() { if (mCi != null && mCi instanceof BaseCommands) { return ((BaseCommands) mCi).mAllowedNetworkTypesBitmask; } else { return RadioAccessFamily.getRafFromNetworkType(RILConstants.PREFERRED_NETWORK_MODE); } } /** * Gets the default SMSC address. * * @param result Callback message contains the SMSC address. */ @UnsupportedAppUsage public void getSmscAddress(Message result) { mCi.getSmscAddress(result); } /** * Sets the default SMSC address. * * @param address new SMSC address * @param result Callback message is empty on completion */ @UnsupportedAppUsage public void setSmscAddress(String address, Message result) { mCi.setSmscAddress(address, result); } /** * setTTYMode * sets a TTY mode option. * @param ttyMode is a one of the following: * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO} * @param onComplete a callback message when the action is completed */ public void setTTYMode(int ttyMode, Message onComplete) { mCi.setTTYMode(ttyMode, onComplete); } /** * setUiTTYMode * sets a TTY mode option. * @param ttyMode is a one of the following: * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO} * @param onComplete a callback message when the action is completed */ public void setUiTTYMode(int uiTtyMode, Message onComplete) { Rlog.d(LOG_TAG, "unexpected setUiTTYMode method call"); } /** * queryTTYMode * query the status of the TTY mode * * @param onComplete a callback message when the action is completed. */ public void queryTTYMode(Message onComplete) { mCi.queryTTYMode(onComplete); } /** * Enable or disable enhanced Voice Privacy (VP). If enhanced VP is * disabled, normal VP is enabled. * * @param enable whether true or false to enable or disable. * @param onComplete a callback message when the action is completed. */ public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) { } /** * Get the currently set Voice Privacy (VP) mode. * * @param onComplete a callback message when the action is completed. */ public void getEnhancedVoicePrivacy(Message onComplete) { } /** * Assign a specified band for RF configuration. * * @param bandMode one of BM_*_BAND * @param response is callback message */ public void setBandMode(int bandMode, Message response) { mCi.setBandMode(bandMode, response); } /** * Query the list of band mode supported by RF. * * @param response is callback message * ((AsyncResult)response.obj).result is an int[] where int[0] is * the size of the array and the rest of each element representing * one available BM_*_BAND */ public void queryAvailableBandMode(Message response) { mCi.queryAvailableBandMode(response); } /** * Read one of the NV items defined in {@link RadioNVItems} / {@code ril_nv_items.h}. * Used for device configuration by some CDMA operators. * * @param itemID the ID of the item to read * @param response callback message with the String response in the obj field * @param workSource calling WorkSource */ public void nvReadItem(int itemID, Message response, WorkSource workSource) { mCi.nvReadItem(itemID, response, workSource); } /** * Write one of the NV items defined in {@link RadioNVItems} / {@code ril_nv_items.h}. * Used for device configuration by some CDMA operators. * * @param itemID the ID of the item to read * @param itemValue the value to write, as a String * @param response Callback message. * @param workSource calling WorkSource */ public void nvWriteItem(int itemID, String itemValue, Message response, WorkSource workSource) { mCi.nvWriteItem(itemID, itemValue, response, workSource); } /** * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage. * Used for device configuration by some CDMA operators. * * @param preferredRoamingList byte array containing the new PRL * @param response Callback message. */ public void nvWriteCdmaPrl(byte[] preferredRoamingList, Message response) { mCi.nvWriteCdmaPrl(preferredRoamingList, response); } /** * Perform the radio modem reboot. The radio will be taken offline. Used for device * configuration by some CDMA operators. * TODO: reuse nvResetConfig for now, should move to separate HAL API. * * @param response Callback message. */ public void rebootModem(Message response) { mCi.nvResetConfig(1 /* 1: reload NV reset, trigger a modem reboot */, response); } /** * Perform the modem configuration reset. Used for device configuration by some CDMA operators. * TODO: reuse nvResetConfig for now, should move to separate HAL API. * * @param response Callback message. */ public void resetModemConfig(Message response) { mCi.nvResetConfig(3 /* factory NV reset */, response); } /** * Perform modem configuration erase. Used for network reset * * @param response Callback message. */ public void eraseModemConfig(Message response) { mCi.nvResetConfig(2 /* erase NV */, response); } /** * Erase data saved in the SharedPreference. Used for network reset * */ public boolean eraseDataInSharedPreferences() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); Rlog.d(LOG_TAG, "Erase all data saved in SharedPreferences"); editor.clear(); return editor.commit(); } public void setSystemSelectionChannels(List specifiers, Message response) { mCi.setSystemSelectionChannels(specifiers, response); } /** * Get which bands the modem's background scan is acting on. * * @param response Callback message. */ public void getSystemSelectionChannels(Message response) { mCi.getSystemSelectionChannels(response); } public void notifyDataActivity() { mNotifier.notifyDataActivity(this); } private void notifyMessageWaitingIndicator() { // Do not notify voice mail waiting if device doesn't support voice if (!mIsVoiceCapable) return; // This function is added to send the notification to DefaultPhoneNotifier. mNotifier.notifyMessageWaitingChanged(this); } /** Send notification with an updated PreciseDataConnectionState to a single data connection */ public void notifyDataConnection(PreciseDataConnectionState state) { mNotifier.notifyDataConnection(this, state); } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void notifyOtaspChanged(int otaspMode) { mOtaspRegistrants.notifyRegistrants(new AsyncResult(null, otaspMode, null)); } public void notifyVoiceActivationStateChanged(int state) { mNotifier.notifyVoiceActivationStateChanged(this, state); } public void notifyDataActivationStateChanged(int state) { mNotifier.notifyDataActivationStateChanged(this, state); } public void notifyUserMobileDataStateChanged(boolean state) { mNotifier.notifyUserMobileDataStateChanged(this, state); } /** Send notification that display info has changed. */ public void notifyDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) { mNotifier.notifyDisplayInfoChanged(this, telephonyDisplayInfo); } public void notifySignalStrength() { mNotifier.notifySignalStrength(this); } public PhoneConstants.DataState getDataConnectionState(String apnType) { return PhoneConstants.DataState.DISCONNECTED; } /** Default implementation to get the PreciseDataConnectionState */ public @Nullable PreciseDataConnectionState getPreciseDataConnectionState(String apnType) { return null; } public void notifyCellInfo(List cellInfo) { AsyncResult ar = new AsyncResult(null, cellInfo, null); mCellInfoRegistrants.notifyRegistrants(ar); mNotifier.notifyCellInfo(this, cellInfo); } /** * Registration point for PhysicalChannelConfig change. * @param h handler to notify * @param what what code of message when delivered * @param obj placed in Message.obj.userObj */ public void registerForPhysicalChannelConfig(Handler h, int what, Object obj) { checkCorrectThread(h); Registrant registrant = new Registrant(h, what, obj); mPhysicalChannelConfigRegistrants.add(registrant); // notify first List physicalChannelConfigs = getPhysicalChannelConfigList(); if (physicalChannelConfigs != null) { registrant.notifyRegistrant(new AsyncResult(null, physicalChannelConfigs, null)); } } public void unregisterForPhysicalChannelConfig(Handler h) { mPhysicalChannelConfigRegistrants.remove(h); } /** Notify {@link PhysicalChannelConfig} changes. */ public void notifyPhysicalChannelConfig(List configs) { mPhysicalChannelConfigRegistrants.notifyRegistrants(new AsyncResult(null, configs, null)); mNotifier.notifyPhysicalChannelConfig(this, configs); } public List getPhysicalChannelConfigList() { return null; } /** * Notify listeners that SRVCC state has changed. */ public void notifySrvccStateChanged(int state) { mNotifier.notifySrvccStateChanged(this, state); } /** Notify the {@link EmergencyNumber} changes. */ public void notifyEmergencyNumberList() { mNotifier.notifyEmergencyNumberList(this); } /** Notify the outgoing Sms {@link EmergencyNumber} changes. */ public void notifyOutgoingEmergencySms(EmergencyNumber emergencyNumber) { mNotifier.notifyOutgoingEmergencySms(this, emergencyNumber); } /** Notify the data enabled changes. */ public void notifyDataEnabled(boolean enabled, @TelephonyManager.DataEnabledReason int reason) { mNotifier.notifyDataEnabled(this, enabled, reason); } /** Notify link capacity estimate has changed. */ public void notifyLinkCapacityEstimateChanged( List linkCapacityEstimateList) { mNotifier.notifyLinkCapacityEstimateChanged(this, linkCapacityEstimateList); } /** * @return true if a mobile originating emergency call is active */ public boolean isInEmergencyCall() { return false; } // This property is used to handle phone process crashes, and is the same for CDMA and IMS // phones protected static boolean getInEcmMode() { if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { return EmergencyStateTracker.getInstance().isInEcm(); } return TelephonyProperties.in_ecm_mode().orElse(false); } /** * @return {@code true} if we are in emergency call back mode. This is a period where the phone * should be using as little power as possible and be ready to receive an incoming call from the * emergency operator. */ public boolean isInEcm() { if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { return EmergencyStateTracker.getInstance().isInEcm(); } return mIsPhoneInEcmState; } public boolean isInImsEcm() { return false; } public boolean isInCdmaEcm() { if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { return EmergencyStateTracker.getInstance().isInCdmaEcm(); } return getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA && isInEcm() && (mImsPhone == null || !mImsPhone.isInImsEcm()); } public void setIsInEcm(boolean isInEcm) { if (!getUnitTestMode()) { TelephonyProperties.in_ecm_mode(isInEcm); } mIsPhoneInEcmState = isInEcm; } /** * @return true if this Phone is in an emergency call that caused emergency callback mode to be * canceled, false if not. */ public boolean isEcmCanceledForEmergency() { return mEcmCanceledForEmergency; } /** * Set whether or not this Phone has an active emergency call that was placed during emergency * callback mode and caused it to be temporarily canceled. * @param isCanceled true if an emergency call was placed that caused ECM to be canceled, false * if it is not in this state. */ public void setEcmCanceledForEmergency(boolean isCanceled) { mEcmCanceledForEmergency = isCanceled; } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private static int getVideoState(Call call) { int videoState = VideoProfile.STATE_AUDIO_ONLY; Connection conn = call.getEarliestConnection(); if (conn != null) { videoState = conn.getVideoState(); } return videoState; } /** * Determines if the specified call currently is or was at some point a video call, or if it is * a conference call. * @param call The call. * @return {@code true} if the call is or was a video call or is a conference call, * {@code false} otherwise. */ private boolean isVideoCallOrConference(Call call) { if (call.isMultiparty()) { return true; } boolean isDowngradedVideoCall = false; if (call instanceof ImsPhoneCall) { ImsPhoneCall imsPhoneCall = (ImsPhoneCall) call; ImsCall imsCall = imsPhoneCall.getImsCall(); return imsCall != null && (imsCall.isVideoCall() || imsCall.wasVideoCall()); } return isDowngradedVideoCall; } /** * @return {@code true} if an IMS video call or IMS conference is present, false otherwise. */ public boolean isImsVideoCallOrConferencePresent() { boolean isPresent = false; if (mImsPhone != null) { isPresent = isVideoCallOrConference(mImsPhone.getForegroundCall()) || isVideoCallOrConference(mImsPhone.getBackgroundCall()) || isVideoCallOrConference(mImsPhone.getRingingCall()); } Rlog.d(LOG_TAG, "isImsVideoCallOrConferencePresent: " + isPresent); return isPresent; } /** * Return a numerical identifier for the phone radio interface. * @return PHONE_TYPE_XXX as defined above. */ @UnsupportedAppUsage public abstract int getPhoneType(); /** * Returns unread voicemail count. This count is shown when the voicemail * notification is expanded.

*/ public int getVoiceMessageCount(){ return mVmCount; } /** sets the voice mail count of the phone and notifies listeners. */ public void setVoiceMessageCount(int countWaiting) { mVmCount = countWaiting; int subId = getSubId(); if (SubscriptionManager.isValidSubscriptionId(subId)) { Rlog.d(LOG_TAG, "setVoiceMessageCount: Storing Voice Mail Count = " + countWaiting + " for mVmCountKey = " + VM_COUNT + subId + " in preferences."); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); SharedPreferences.Editor editor = sp.edit(); editor.putInt(VM_COUNT + subId, countWaiting); editor.apply(); } else { Rlog.e(LOG_TAG, "setVoiceMessageCount in sharedPreference: invalid subId " + subId); } // store voice mail count in SIM IccRecords records = UiccController.getInstance().getIccRecords( mPhoneId, UiccController.APP_FAM_3GPP); if (records != null) { Rlog.d(LOG_TAG, "setVoiceMessageCount: updating SIM Records"); records.setVoiceMessageWaiting(1, countWaiting); } else { Rlog.d(LOG_TAG, "setVoiceMessageCount: SIM Records not found"); } // notify listeners of voice mail notifyMessageWaitingIndicator(); } /** gets the voice mail count from preferences */ protected int getStoredVoiceMessageCount() { int countVoiceMessages = 0; int subId = getSubId(); if (SubscriptionManager.isValidSubscriptionId(subId)) { int invalidCount = -2; //-1 is not really invalid. It is used for unknown number of vm SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); int countFromSP = sp.getInt(VM_COUNT + subId, invalidCount); if (countFromSP != invalidCount) { countVoiceMessages = countFromSP; Rlog.d(LOG_TAG, "getStoredVoiceMessageCount: from preference for subId " + subId + "= " + countVoiceMessages); } else { // Check for old preference if count not found for current subId. This part of the // code is needed only when upgrading from M to N. String subscriberId = sp.getString(VM_ID, null); if (subscriberId != null) { String currentSubscriberId = getSubscriberId(); if (currentSubscriberId != null && currentSubscriberId.equals(subscriberId)) { // get voice mail count from preferences countVoiceMessages = sp.getInt(VM_COUNT, 0); setVoiceMessageCount(countVoiceMessages); Rlog.d(LOG_TAG, "getStoredVoiceMessageCount: from preference = " + countVoiceMessages); } else { Rlog.d(LOG_TAG, "getStoredVoiceMessageCount: returning 0 as count for " + "matching subscriberId not found"); } // get rid of old preferences. SharedPreferences.Editor editor = sp.edit(); editor.remove(VM_ID); editor.remove(VM_COUNT); editor.apply(); } } } else { Rlog.e(LOG_TAG, "getStoredVoiceMessageCount: invalid subId " + subId); } return countVoiceMessages; } /** * send secret dialer codes to launch arbitrary activities. * an Intent is started with the android_secret_code:// URI. * * @param code stripped version of secret code without *#*# prefix and #*#* suffix */ public void sendDialerSpecialCode(String code) { if (!TextUtils.isEmpty(code)) { final BroadcastOptions options = BroadcastOptions.makeBasic(); options.setBackgroundActivityStartsAllowed(true); Intent intent = new Intent(TelephonyIntents.SECRET_CODE_ACTION, Uri.parse("android_secret_code://" + code)); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendBroadcast(intent, null, options.toBundle()); // {@link TelephonyManager.ACTION_SECRET_CODE} will replace {@link // TelephonyIntents#SECRET_CODE_ACTION} in the next Android version. Before // that both of these two actions will be broadcast. Intent secrectCodeIntent = new Intent(TelephonyManager.ACTION_SECRET_CODE, Uri.parse("android_secret_code://" + code)); secrectCodeIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendBroadcast(secrectCodeIntent, null, options.toBundle()); } } /** * Returns the CDMA ERI icon index to display */ public int getCdmaEriIconIndex() { return -1; } /** * Returns the CDMA ERI icon mode, * 0 - ON * 1 - FLASHING */ public int getCdmaEriIconMode() { return -1; } /** * Returns the CDMA ERI text, */ public String getCdmaEriText() { return "GSM nw, no ERI"; } /** * Retrieves the MIN for CDMA phones. */ public String getCdmaMin() { return null; } /** * Check if subscription data has been assigned to mMin * * return true if MIN info is ready; false otherwise. */ public boolean isMinInfoReady() { return false; } /** * Retrieves PRL Version for CDMA phones */ public String getCdmaPrlVersion(){ return null; } /** * @return {@code true} if data is suspended. */ public boolean isDataSuspended() { return false; } /** * send burst DTMF tone, it can send the string as single character or multiple character * ignore if there is no active call or not valid digits string. * Valid digit means only includes characters ISO-LATIN characters 0-9, *, # * The difference between sendDtmf and sendBurstDtmf is sendDtmf only sends one character, * this api can send single character and multiple character, also, this api has response * back to caller. * * @param dtmfString is string representing the dialing digit(s) in the active call * @param on the DTMF ON length in milliseconds, or 0 for default * @param off the DTMF OFF length in milliseconds, or 0 for default * @param onComplete is the callback message when the action is processed by BP * */ public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) { } /** * Sets an event to be fired when the telephony system processes * a post-dial character on an outgoing call.

* * Messages of type what will be sent to h. * The obj field of these Message's will be instances of * AsyncResult. Message.obj.result will be * a Connection object.

* * Message.arg1 will be the post dial character being processed, * or 0 ('\0') if end of string.

* * If Connection.getPostDialState() == WAIT, * the application must call * {@link com.android.internal.telephony.Connection#proceedAfterWaitChar() * Connection.proceedAfterWaitChar()} or * {@link com.android.internal.telephony.Connection#cancelPostDial() * Connection.cancelPostDial()} * for the telephony system to continue playing the post-dial * DTMF sequence.

* * If Connection.getPostDialState() == WILD, * the application must call * {@link com.android.internal.telephony.Connection#proceedAfterWildChar * Connection.proceedAfterWildChar()} * or * {@link com.android.internal.telephony.Connection#cancelPostDial() * Connection.cancelPostDial()} * for the telephony system to continue playing the * post-dial DTMF sequence.

* * Only one post dial character handler may be set.

* Calling this method with "h" equal to null unsets this handler.

*/ @UnsupportedAppUsage public void setOnPostDialCharacter(Handler h, int what, Object obj) { mPostDialHandler = new Registrant(h, what, obj); } public Registrant getPostDialHandler() { return mPostDialHandler; } /** * request to exit emergency call back mode * the caller should use setOnECMModeExitResponse * to receive the emergency callback mode exit response */ @UnsupportedAppUsage public void exitEmergencyCallbackMode() { } /** * Register for notifications when CDMA OTA Provision status change * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) { } /** * Unregister for notifications when CDMA OTA Provision status change * @param h Handler to be removed from the registrant list. */ public void unregisterForCdmaOtaStatusChange(Handler h) { } /** * Registration point for subscription info ready * @param h handler to notify * @param what what code of message when delivered * @param obj placed in Message.obj */ public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) { } /** * Unregister for notifications for subscription info * @param h Handler to be removed from the registrant list. */ public void unregisterForSubscriptionInfoReady(Handler h) { } /** * Returns true if OTA Service Provisioning needs to be performed. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean needsOtaServiceProvisioning() { return false; } /** * this decides if the dial number is OTA(Over the air provision) number or not * @param dialStr is string representing the dialing digit(s) * @return true means the dialStr is OTA number, and false means the dialStr is not OTA number */ public boolean isOtaSpNumber(String dialStr) { return false; } /** * Register for notifications when OTA Service Provisioning mode has changed. * *

The mode is integer. {@link TelephonyManager#OTASP_UNKNOWN} * means the value is currently unknown and the system should wait until * {@link TelephonyManager#OTASP_NEEDED} or {@link TelephonyManager#OTASP_NOT_NEEDED} is * received before making the decision to perform OTASP or not. * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForOtaspChange(Handler h, int what, Object obj) { checkCorrectThread(h); mOtaspRegistrants.addUnique(h, what, obj); // notify first new Registrant(h, what, obj).notifyRegistrant(new AsyncResult(null, getOtasp(), null)); } /** * Unegister for notifications when OTA Service Provisioning mode has changed. * @param h Handler to be removed from the registrant list. */ public void unregisterForOtaspChange(Handler h) { mOtaspRegistrants.remove(h); } /** * Returns the current OTA Service Provisioning mode. * * @see registerForOtaspChange */ public int getOtasp() { return TelephonyManager.OTASP_UNKNOWN; } /** * Register for notifications when CDMA call waiting comes * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForCallWaiting(Handler h, int what, Object obj){ } /** * Unegister for notifications when CDMA Call waiting comes * @param h Handler to be removed from the registrant list. */ public void unregisterForCallWaiting(Handler h){ } /** * Registration point for Ecm timer reset * @param h handler to notify * @param what user-defined message code * @param obj placed in Message.obj */ @UnsupportedAppUsage public void registerForEcmTimerReset(Handler h, int what, Object obj) { } /** * Unregister for notification for Ecm timer reset * @param h Handler to be removed from the registrant list. */ @UnsupportedAppUsage public void unregisterForEcmTimerReset(Handler h) { } /** * Register for signal information notifications from the network. * Message.obj will contain an AsyncResult. * AsyncResult.result will be a SuppServiceNotification instance. * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForSignalInfo(Handler h, int what, Object obj) { mCi.registerForSignalInfo(h, what, obj); } /** * Unregisters for signal information notifications. * Extraneous calls are tolerated silently * * @param h Handler to be removed from the registrant list. */ public void unregisterForSignalInfo(Handler h) { mCi.unregisterForSignalInfo(h); } /** * Register for display information notifications from the network. * Message.obj will contain an AsyncResult. * AsyncResult.result will be a SuppServiceNotification instance. * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForDisplayInfo(Handler h, int what, Object obj) { mCi.registerForDisplayInfo(h, what, obj); } /** * Unregisters for display information notifications. * Extraneous calls are tolerated silently * * @param h Handler to be removed from the registrant list. */ public void unregisterForDisplayInfo(Handler h) { mCi.unregisterForDisplayInfo(h); } /** * Register for CDMA number information record notification from the network. * Message.obj will contain an AsyncResult. * AsyncResult.result will be a CdmaInformationRecords.CdmaNumberInfoRec * instance. * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForNumberInfo(Handler h, int what, Object obj) { mCi.registerForNumberInfo(h, what, obj); } /** * Unregisters for number information record notifications. * Extraneous calls are tolerated silently * * @param h Handler to be removed from the registrant list. */ public void unregisterForNumberInfo(Handler h) { mCi.unregisterForNumberInfo(h); } /** * Register for CDMA redirected number information record notification * from the network. * Message.obj will contain an AsyncResult. * AsyncResult.result will be a CdmaInformationRecords.CdmaRedirectingNumberInfoRec * instance. * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForRedirectedNumberInfo(Handler h, int what, Object obj) { mCi.registerForRedirectedNumberInfo(h, what, obj); } /** * Unregisters for redirected number information record notification. * Extraneous calls are tolerated silently * * @param h Handler to be removed from the registrant list. */ public void unregisterForRedirectedNumberInfo(Handler h) { mCi.unregisterForRedirectedNumberInfo(h); } /** * Register for CDMA line control information record notification * from the network. * Message.obj will contain an AsyncResult. * AsyncResult.result will be a CdmaInformationRecords.CdmaLineControlInfoRec * instance. * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForLineControlInfo(Handler h, int what, Object obj) { mCi.registerForLineControlInfo(h, what, obj); } /** * Unregisters for line control information notifications. * Extraneous calls are tolerated silently * * @param h Handler to be removed from the registrant list. */ public void unregisterForLineControlInfo(Handler h) { mCi.unregisterForLineControlInfo(h); } /** * Register for CDMA T53 CLIR information record notifications * from the network. * Message.obj will contain an AsyncResult. * AsyncResult.result will be a CdmaInformationRecords.CdmaT53ClirInfoRec * instance. * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerFoT53ClirlInfo(Handler h, int what, Object obj) { mCi.registerFoT53ClirlInfo(h, what, obj); } /** * Unregisters for T53 CLIR information record notification * Extraneous calls are tolerated silently * * @param h Handler to be removed from the registrant list. */ public void unregisterForT53ClirInfo(Handler h) { mCi.unregisterForT53ClirInfo(h); } /** * Register for CDMA T53 audio control information record notifications * from the network. * Message.obj will contain an AsyncResult. * AsyncResult.result will be a CdmaInformationRecords.CdmaT53AudioControlInfoRec * instance. * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForT53AudioControlInfo(Handler h, int what, Object obj) { mCi.registerForT53AudioControlInfo(h, what, obj); } /** * Unregisters for T53 audio control information record notifications. * Extraneous calls are tolerated silently * * @param h Handler to be removed from the registrant list. */ public void unregisterForT53AudioControlInfo(Handler h) { mCi.unregisterForT53AudioControlInfo(h); } /** * registers for exit emergency call back mode request response * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ @UnsupportedAppUsage public void setOnEcbModeExitResponse(Handler h, int what, Object obj){ } /** * Unregisters for exit emergency call back mode request response * * @param h Handler to be removed from the registrant list. */ @UnsupportedAppUsage public void unsetOnEcbModeExitResponse(Handler h){ } /** * Register for radio off or not available * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForRadioOffOrNotAvailable(Handler h, int what, Object obj) { mRadioOffOrNotAvailableRegistrants.addUnique(h, what, obj); } /** * Unregisters for radio off or not available * * @param h Handler to be removed from the registrant list. */ public void unregisterForRadioOffOrNotAvailable(Handler h) { mRadioOffOrNotAvailableRegistrants.remove(h); } /** * Location to an updatable file listing carrier provisioning urls. * An example: * * * * http://myserver.com/foo?mdn=%3$s&iccid=%1$s&imei=%2$s * */ private static final String PROVISIONING_URL_PATH = "/data/misc/radio/provisioning_urls.xml"; private final File mProvisioningUrlFile = new File(PROVISIONING_URL_PATH); /** XML tag for root element. */ private static final String TAG_PROVISIONING_URLS = "provisioningUrls"; /** XML tag for individual url */ private static final String TAG_PROVISIONING_URL = "provisioningUrl"; /** XML attribute for mcc */ private static final String ATTR_MCC = "mcc"; /** XML attribute for mnc */ private static final String ATTR_MNC = "mnc"; private String getProvisioningUrlBaseFromFile() { XmlPullParser parser; final Configuration config = mContext.getResources().getConfiguration(); try (FileReader fileReader = new FileReader(mProvisioningUrlFile)) { parser = Xml.newPullParser(); parser.setInput(fileReader); XmlUtils.beginDocument(parser, TAG_PROVISIONING_URLS); while (true) { XmlUtils.nextElement(parser); final String element = parser.getName(); if (element == null) break; if (element.equals(TAG_PROVISIONING_URL)) { String mcc = parser.getAttributeValue(null, ATTR_MCC); try { if (mcc != null && Integer.parseInt(mcc) == config.mcc) { String mnc = parser.getAttributeValue(null, ATTR_MNC); if (mnc != null && Integer.parseInt(mnc) == config.mnc) { parser.next(); if (parser.getEventType() == XmlPullParser.TEXT) { return parser.getText(); } } } } catch (NumberFormatException e) { Rlog.e(LOG_TAG, "Exception in getProvisioningUrlBaseFromFile: " + e); } } } return null; } catch (FileNotFoundException e) { Rlog.e(LOG_TAG, "Carrier Provisioning Urls file not found"); } catch (XmlPullParserException e) { Rlog.e(LOG_TAG, "Xml parser exception reading Carrier Provisioning Urls file: " + e); } catch (IOException e) { Rlog.e(LOG_TAG, "I/O exception reading Carrier Provisioning Urls file: " + e); } return null; } /** * Get the mobile provisioning url. */ public String getMobileProvisioningUrl() { String url = getProvisioningUrlBaseFromFile(); if (TextUtils.isEmpty(url)) { url = mContext.getResources().getString(R.string.mobile_provisioning_url); Rlog.d(LOG_TAG, "getMobileProvisioningUrl: url from resource =" + url); } else { Rlog.d(LOG_TAG, "getMobileProvisioningUrl: url from File =" + url); } // Populate the iccid, imei and phone number in the provisioning url. if (!TextUtils.isEmpty(url)) { String phoneNumber = getLine1Number(); if (TextUtils.isEmpty(phoneNumber)) { phoneNumber = "0000000000"; } url = String.format(url, getIccSerialNumber() /* ICCID */, getDeviceId() /* IMEI */, phoneNumber /* Phone number */); } return url; } /** * Check if there are matching tethering (i.e DUN) for the carrier. * @return true if there is a matching DUN APN. */ public boolean hasMatchedTetherApnSetting() { NetworkRegistrationInfo nrs = getServiceState().getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); if (nrs != null) { return getDataNetworkController().getDataProfileManager() .isTetheringDataProfileExisting(nrs.getAccessNetworkTechnology()); } return false; } /** * Report on whether data connectivity is allowed for internet. * * @return {@code true} if internet data is allowed to be established. */ public boolean isDataAllowed() { return getDataNetworkController().isInternetDataAllowed(false/* ignoreExistingNetworks */); } /** * Action set from carrier signalling broadcast receivers to enable/disable metered apns. */ public void carrierActionSetMeteredApnsEnabled(boolean enabled) { mCarrierActionAgent.carrierActionSetMeteredApnsEnabled(enabled); } /** * Action set from carrier signalling broadcast receivers to enable/disable radio */ public void carrierActionSetRadioEnabled(boolean enabled) { mCarrierActionAgent.carrierActionSetRadioEnabled(enabled); } /** * Action set from carrier app to start/stop reporting default network condition. */ public void carrierActionReportDefaultNetworkStatus(boolean report) { mCarrierActionAgent.carrierActionReportDefaultNetworkStatus(report); } /** * Action set from carrier signalling broadcast receivers to reset all carrier actions */ public void carrierActionResetAll() { mCarrierActionAgent.carrierActionReset(); } /** * Notify registrants of a new ringing Connection. * Subclasses of Phone probably want to replace this with a * version scoped to their packages */ public void notifyNewRingingConnectionP(Connection cn) { Rlog.i(LOG_TAG, String.format( "notifyNewRingingConnection: phoneId=[%d], connection=[%s], registrants=[%s]", getPhoneId(), cn, getNewRingingConnectionRegistrantsAsString())); if (!mIsVoiceCapable) return; AsyncResult ar = new AsyncResult(null, cn, null); mNewRingingConnectionRegistrants.notifyRegistrants(ar); } /** * helper for notifyNewRingingConnectionP(Connection) to create a string for a log message. * * @return a list of objects in mNewRingingConnectionRegistrants as a String */ private String getNewRingingConnectionRegistrantsAsString() { List registrants = new ArrayList<>(); for (int i = 0; i < mNewRingingConnectionRegistrants.size(); i++) { registrants.add(mNewRingingConnectionRegistrants.get(i).toString()); } return String.join(", ", registrants); } /** * Notify registrants of a new unknown connection. */ public void notifyUnknownConnectionP(Connection cn) { mUnknownConnectionRegistrants.notifyResult(cn); } /** * Notify registrants if phone is video capable. */ public void notifyForVideoCapabilityChanged(boolean isVideoCallCapable) { // Cache the current video capability so that we don't lose the information. mIsVideoCapable = isVideoCallCapable; AsyncResult ar = new AsyncResult(null, isVideoCallCapable, null); mVideoCapabilityChangedRegistrants.notifyRegistrants(ar); } /** * Notify registrants of a RING event. */ private void notifyIncomingRing() { if (!mIsVoiceCapable) return; AsyncResult ar = new AsyncResult(null, this, null); mIncomingRingRegistrants.notifyRegistrants(ar); } /** * Send the incoming call Ring notification if conditions are right. */ private void sendIncomingCallRingNotification(int token) { if (mIsVoiceCapable && !mDoesRilSendMultipleCallRing && (token == mCallRingContinueToken)) { Rlog.d(LOG_TAG, "Sending notifyIncomingRing"); notifyIncomingRing(); sendMessageDelayed( obtainMessage(EVENT_CALL_RING_CONTINUE, token, 0), mCallRingDelay); } else { Rlog.d(LOG_TAG, "Ignoring ring notification request," + " mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing + " token=" + token + " mCallRingContinueToken=" + mCallRingContinueToken + " mIsVoiceCapable=" + mIsVoiceCapable); } } /** * Enable or disable always reporting signal strength changes from radio. * * @param isEnable {@code true} for enabling; {@code false} for disabling. */ public void setAlwaysReportSignalStrength(boolean isEnable) { if (mDeviceStateMonitor != null) { mDeviceStateMonitor.setAlwaysReportSignalStrength(isEnable); } } /** * TODO: Adding a function for each property is not good. * A fucntion of type getPhoneProp(propType) where propType is an * enum of GSM+CDMA+LTE props would be a better approach. * * Get "Restriction of menu options for manual PLMN selection" bit * status from EF_CSP data, this belongs to "Value Added Services Group". * @return true if this bit is set or EF_CSP data is unavailable, * false otherwise */ @UnsupportedAppUsage public boolean isCspPlmnEnabled() { return false; } /** * Return an interface to retrieve the ISIM records for IMS, if available. * @return the interface to retrieve the ISIM records, or null if not supported */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public IsimRecords getIsimRecords() { Rlog.e(LOG_TAG, "getIsimRecords() is only supported on LTE devices"); return null; } /** * Retrieves the MSISDN from the UICC. For GSM/UMTS phones, this is equivalent to * {@link #getLine1Number()}. For CDMA phones, {@link #getLine1Number()} returns * the MDN, so this method is provided to return the MSISDN on CDMA/LTE phones. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public String getMsisdn() { return null; } /** * Retrieves the EF_PNN from the UICC For GSM/UMTS phones. */ public String getPlmn() { return null; } public void notifyCallForwardingIndicator() { } /** * 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 */ public void setVoiceMessageWaiting(int line, int countWaiting) { // This function should be overridden by class GsmCdmaPhone. Rlog.e(LOG_TAG, "Error! This function should never be executed, inactive Phone."); } /** * Gets the USIM service table from the UICC, if present and available. * @return an interface to the UsimServiceTable record, or null if not available */ public UsimServiceTable getUsimServiceTable() { IccRecords r = mIccRecords.get(); return (r != null) ? r.getUsimServiceTable() : null; } /** * Gets the Uicc card corresponding to this phone. * @return the UiccCard object corresponding to the phone ID. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public UiccCard getUiccCard() { return mUiccController.getUiccCard(mPhoneId); } /** * Gets the Uicc port corresponding to this phone. * @return the UiccPort object corresponding to the phone ID. */ public UiccPort getUiccPort() { return mUiccController.getUiccPort(mPhoneId); } /** * Set IMS registration state */ public void setImsRegistrationState(boolean registered) { } /** * Return an instance of a IMS phone */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public Phone getImsPhone() { return mImsPhone; } @VisibleForTesting public void setImsPhone(ImsPhone imsPhone) { mImsPhone = imsPhone; } /** * Returns Carrier specific information that will be used to encrypt the IMSI and IMPI. * @param keyType whether the key is being used for WLAN or ePDG. * @param fallback whether or not to fall back to the encryption key info stored in carrier * config * @return ImsiEncryptionInfo which includes the Key Type, the Public Key * {@link java.security.PublicKey} and the Key Identifier. * The keyIdentifier This is used by the server to help it locate the private key to * decrypt the permanent identity. */ public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType, boolean fallback) { return null; } /** * Sets the carrier information needed to encrypt the IMSI and IMPI. * @param imsiEncryptionInfo Carrier specific information that will be used to encrypt the * IMSI and IMPI. This includes the Key type, the Public key * {@link java.security.PublicKey} and the Key identifier. */ public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) { return; } /** * Deletes all the keys for a given Carrier from the device keystore. * @param carrierId : the carrier ID which needs to be matched in the delete query */ public void deleteCarrierInfoForImsiEncryption(int carrierId) { return; } /** * Deletes all the keys for a given Carrier from the device keystore. * @param carrierId : the carrier ID which needs to be matched in the delete query * @param simOperator : MccMnc which needs to be matched in the delete query. */ public void deleteCarrierInfoForImsiEncryption(int carrierId, String simOperator) { } public int getCarrierId() { return TelephonyManager.UNKNOWN_CARRIER_ID; } public String getCarrierName() { return null; } public int getMNOCarrierId() { return TelephonyManager.UNKNOWN_CARRIER_ID; } public int getSpecificCarrierId() { return TelephonyManager.UNKNOWN_CARRIER_ID; } public String getSpecificCarrierName() { return null; } public int getCarrierIdListVersion() { return TelephonyManager.UNKNOWN_CARRIER_ID_LIST_VERSION; } public int getEmergencyNumberDbVersion() { return TelephonyManager.INVALID_EMERGENCY_NUMBER_DB_VERSION; } public void resolveSubscriptionCarrierId(String simState) { } /** * Resets the Carrier Keys in the database. This involves 2 steps: * 1. Delete the keys from the database. * 2. Send an intent to download new Certificates. */ public void resetCarrierKeysForImsiEncryption() { return; } /** * Return if UT capability of ImsPhone is enabled or not */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isUtEnabled() { if (mImsPhone != null) { return mImsPhone.isUtEnabled(); } return false; } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void dispose() { } /** * Dials a number. * * @param dialString The number to dial. * @param dialArgs Parameters to dial with. * @return The Connection. * @throws CallStateException */ protected Connection dialInternal(String dialString, DialArgs dialArgs) throws CallStateException { // dialInternal shall be overriden by GsmCdmaPhone return null; } /* * This function is for CSFB SS. GsmCdmaPhone overrides this function. */ public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) { } public void queryCLIP(Message onComplete) { } /* * Returns the subscription id. */ @UnsupportedAppUsage public int getSubId() { return mSubscriptionManagerService.getSubId(mPhoneId); } /** * Returns the phone id. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getPhoneId() { return mPhoneId; } /** * Override the service provider name and the operator name for the current ICCID. */ public boolean setOperatorBrandOverride(String brand) { return false; } /** * Override the roaming indicator for the current ICCID. */ public boolean setRoamingOverride(List gsmRoamingList, List gsmNonRoamingList, List cdmaRoamingList, List cdmaNonRoamingList) { String iccId = getIccSerialNumber(); if (TextUtils.isEmpty(iccId)) { return false; } setRoamingOverrideHelper(gsmRoamingList, GSM_ROAMING_LIST_OVERRIDE_PREFIX, iccId); setRoamingOverrideHelper(gsmNonRoamingList, GSM_NON_ROAMING_LIST_OVERRIDE_PREFIX, iccId); setRoamingOverrideHelper(cdmaRoamingList, CDMA_ROAMING_LIST_OVERRIDE_PREFIX, iccId); setRoamingOverrideHelper(cdmaNonRoamingList, CDMA_NON_ROAMING_LIST_OVERRIDE_PREFIX, iccId); // Refresh. ServiceStateTracker tracker = getServiceStateTracker(); if (tracker != null) { tracker.pollState(); } return true; } private void setRoamingOverrideHelper(List list, String prefix, String iccId) { SharedPreferences.Editor spEditor = PreferenceManager.getDefaultSharedPreferences(mContext).edit(); String key = prefix + iccId; if (list == null || list.isEmpty()) { spEditor.remove(key).commit(); } else { spEditor.putStringSet(key, new HashSet(list)).commit(); } } public boolean isMccMncMarkedAsRoaming(String mccMnc) { return getRoamingOverrideHelper(GSM_ROAMING_LIST_OVERRIDE_PREFIX, mccMnc); } public boolean isMccMncMarkedAsNonRoaming(String mccMnc) { return getRoamingOverrideHelper(GSM_NON_ROAMING_LIST_OVERRIDE_PREFIX, mccMnc); } public boolean isSidMarkedAsRoaming(int SID) { return getRoamingOverrideHelper(CDMA_ROAMING_LIST_OVERRIDE_PREFIX, Integer.toString(SID)); } public boolean isSidMarkedAsNonRoaming(int SID) { return getRoamingOverrideHelper(CDMA_NON_ROAMING_LIST_OVERRIDE_PREFIX, Integer.toString(SID)); } /** * Query the IMS Registration Status. * * @return true if IMS is Registered */ public boolean isImsRegistered() { Phone imsPhone = mImsPhone; boolean isImsRegistered = false; if (imsPhone != null) { isImsRegistered = imsPhone.isImsRegistered(); } else { ServiceStateTracker sst = getServiceStateTracker(); if (sst != null) { isImsRegistered = sst.isImsRegistered(); } } Rlog.d(LOG_TAG, "isImsRegistered =" + isImsRegistered); return isImsRegistered; } /** * Get Wifi Calling Feature Availability */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isWifiCallingEnabled() { Phone imsPhone = mImsPhone; boolean isWifiCallingEnabled = false; if (imsPhone != null) { isWifiCallingEnabled = imsPhone.isWifiCallingEnabled(); } Rlog.d(LOG_TAG, "isWifiCallingEnabled =" + isWifiCallingEnabled); return isWifiCallingEnabled; } /** * @return true if the IMS capability for the registration technology specified is available, * false otherwise. */ public boolean isImsCapabilityAvailable(int capability, int regTech) throws ImsException { Phone imsPhone = mImsPhone; boolean isAvailable = false; if (imsPhone != null) { isAvailable = imsPhone.isImsCapabilityAvailable(capability, regTech); } Rlog.d(LOG_TAG, "isImsCapabilityAvailable, capability=" + capability + ", regTech=" + regTech + ", isAvailable=" + isAvailable); return isAvailable; } /** * Get Volte Feature Availability * @deprecated Use {@link #isVoiceOverCellularImsEnabled} instead. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Deprecated public boolean isVolteEnabled() { return isVoiceOverCellularImsEnabled(); } /** * @return {@code true} if voice over IMS on cellular is enabled, {@code false} otherwise. */ public boolean isVoiceOverCellularImsEnabled() { Phone imsPhone = mImsPhone; boolean isVolteEnabled = false; if (imsPhone != null) { isVolteEnabled = imsPhone.isVoiceOverCellularImsEnabled(); } Rlog.d(LOG_TAG, "isVoiceOverCellularImsEnabled=" + isVolteEnabled); return isVolteEnabled; } /** * @return the IMS MmTel Registration technology for this Phone, defined in * {@link ImsRegistrationImplBase}. */ public int getImsRegistrationTech() { Phone imsPhone = mImsPhone; int regTech = ImsRegistrationImplBase.REGISTRATION_TECH_NONE; if (imsPhone != null) { regTech = imsPhone.getImsRegistrationTech(); } Rlog.d(LOG_TAG, "getImsRegistrationTechnology =" + regTech); return regTech; } /** * Get the IMS MmTel Registration technology for this Phone, defined in * {@link ImsRegistrationImplBase}. */ public void getImsRegistrationTech(Consumer callback) { Phone imsPhone = mImsPhone; if (imsPhone != null) { imsPhone.getImsRegistrationTech(callback); } else { callback.accept(ImsRegistrationImplBase.REGISTRATION_TECH_NONE); } } /** * Asynchronously get the IMS MmTel Registration state for this Phone. */ public void getImsRegistrationState(Consumer callback) { Phone imsPhone = mImsPhone; if (imsPhone != null) { imsPhone.getImsRegistrationState(callback); } else { callback.accept(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); } } private boolean getRoamingOverrideHelper(String prefix, String key) { String iccId = getIccSerialNumber(); if (TextUtils.isEmpty(iccId) || TextUtils.isEmpty(key)) { return false; } SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); Set value = sp.getStringSet(prefix + iccId, null); if (value == null) { return false; } return value.contains(key); } /** * @return returns the latest radio state from the modem */ public int getRadioPowerState() { return mCi.getRadioState(); } /** * Is Radio Present on the device and is it accessible */ public boolean isRadioAvailable() { return mCi.getRadioState() != TelephonyManager.RADIO_POWER_UNAVAILABLE; } /** * Is Radio turned on */ public boolean isRadioOn() { return mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON; } /** * shutdown Radio gracefully */ public void shutdownRadio() { getServiceStateTracker().requestShutdown(); } /** * Return true if the device is shutting down. */ public boolean isShuttingDown() { return getServiceStateTracker().isDeviceShuttingDown(); } /** * Set phone radio capability * * @param rc the phone radio capability defined in * RadioCapability. It's a input object used to transfer parameter to logic modem * @param response Callback message. */ public void setRadioCapability(RadioCapability rc, Message response) { mCi.setRadioCapability(rc, response); } /** * Get phone radio access family * * @return a bit mask to identify the radio access family. */ public int getRadioAccessFamily() { final RadioCapability rc = getRadioCapability(); return (rc == null ? RadioAccessFamily.RAF_UNKNOWN : rc.getRadioAccessFamily()); } /** * Get the associated data modems Id. * * @return a String containing the id of the data modem */ public String getModemUuId() { final RadioCapability rc = getRadioCapability(); return (rc == null ? "" : rc.getLogicalModemUuid()); } /** * Get phone radio capability * * @return the capability of the radio defined in RadioCapability */ public RadioCapability getRadioCapability() { return mRadioCapability.get(); } /** * The RadioCapability has changed. This comes up from the RIL and is called when radios first * become available or after a capability switch. The flow is we use setRadioCapability to * request a change with the RIL and get an UNSOL response with the new data which gets set * here. * * @param rc the phone radio capability currently in effect for this phone. * @param capabilitySwitched whether this method called after a radio capability switch * completion or called when radios first become available. */ public void radioCapabilityUpdated(RadioCapability rc, boolean capabilitySwitched) { // Called when radios first become available or after a capability switch // Update the cached value mRadioCapability.set(rc); if (SubscriptionManager.isValidSubscriptionId(getSubId())) { boolean restoreSelection = !mContext.getResources().getBoolean( com.android.internal.R.bool.skip_restoring_network_selection); sendSubscriptionSettings(restoreSelection); } // When radio capability switch is done, query IMEI value and update it in Phone objects // to make it in sync with the IMEI value currently used by Logical-Modem. if (capabilitySwitched) { mCi.getImei(obtainMessage(EVENT_GET_DEVICE_IMEI_DONE)); mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE)); } } public void sendSubscriptionSettings(boolean restoreNetworkSelection) { // Send settings down if (mIsAllowedNetworkTypesLoadedFromDb) { updateAllowedNetworkTypes(null); } if (restoreNetworkSelection) { restoreSavedNetworkSelection(null); } updateUsageSetting(); } private int getResolvedUsageSetting(int subId) { SubscriptionInfo subInfo = null; SubscriptionInfoInternal subInfoInternal = mSubscriptionManagerService .getSubscriptionInfoInternal(subId); if (subInfoInternal != null) { subInfo = subInfoInternal.toSubscriptionInfo(); } if (subInfo == null) { loge("Failed to get SubscriptionInfo for subId=" + subId); return SubscriptionManager.USAGE_SETTING_UNKNOWN; } if (subInfo.getUsageSetting() == SubscriptionManager.USAGE_SETTING_UNKNOWN) { return SubscriptionManager.USAGE_SETTING_UNKNOWN; } if (subInfo.getUsageSetting() != SubscriptionManager.USAGE_SETTING_DEFAULT) { return subInfo.getUsageSetting(); } if (subInfo.isOpportunistic()) { return SubscriptionManager.USAGE_SETTING_DATA_CENTRIC; } else { return mContext.getResources().getInteger( com.android.internal.R.integer.config_default_cellular_usage_setting); } } /** * Attempt to update the usage setting. * * @return whether the usage setting will be updated (used for test) */ public boolean updateUsageSetting() { if (!mIsUsageSettingSupported) return false; final int subId = getSubId(); if (!SubscriptionManager.isValidSubscriptionId(subId)) return false; final int lastPreferredUsageSetting = mPreferredUsageSetting; mPreferredUsageSetting = getResolvedUsageSetting(subId); if (mPreferredUsageSetting == SubscriptionManager.USAGE_SETTING_UNKNOWN) { loge("Usage Setting is Supported but Preferred Setting Unknown!"); return false; } // We might get a lot of requests to update, so definitely we don't want to hammer // the modem with multiple duplicate requests for usage setting updates if (mPreferredUsageSetting == lastPreferredUsageSetting) return false; String logStr = "mPreferredUsageSetting=" + mPreferredUsageSetting + ", lastPreferredUsageSetting=" + lastPreferredUsageSetting + ", mUsageSettingFromModem=" + mUsageSettingFromModem; logd(logStr); mLocalLog.log(logStr); // If the modem value hasn't been updated, request it. if (mUsageSettingFromModem == SubscriptionManager.USAGE_SETTING_UNKNOWN) { mCi.getUsageSetting(obtainMessage(EVENT_GET_USAGE_SETTING_DONE)); // If the modem value is already known, and the value has changed, proceed to update. } else if (mPreferredUsageSetting != mUsageSettingFromModem) { mCi.setUsageSetting(obtainMessage(EVENT_SET_USAGE_SETTING_DONE), mPreferredUsageSetting); } return true; } /** * Registers the handler when phone radio capability is changed. * * @param h Handler for notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForRadioCapabilityChanged(Handler h, int what, Object obj) { mCi.registerForRadioCapabilityChanged(h, what, obj); } /** * Unregister for notifications when phone radio type and access technology is changed. * * @param h Handler to be removed from the registrant list. */ public void unregisterForRadioCapabilityChanged(Handler h) { mCi.unregisterForRadioCapabilityChanged(this); } /** * Determines if the connection to IMS services are available yet. * @return {@code true} if the connection to IMS services are available. */ public boolean isImsAvailable() { if (mImsPhone == null) { return false; } return mImsPhone.isImsAvailable(); } /** * Determines if video calling is enabled for the phone. * * @return {@code true} if video calling is enabled, {@code false} otherwise. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isVideoEnabled() { Phone imsPhone = mImsPhone; if (imsPhone != null) { return imsPhone.isVideoEnabled(); } return false; } /** * Returns the modem activity information */ public void getModemActivityInfo(Message response, WorkSource workSource) { mCi.getModemActivityInfo(response, workSource); } /** * Control the data throttling at modem. * * @param result Message that will be sent back to the requester * @param workSource calling Worksource * @param dataThrottlingAction the DataThrottlingAction that is being requested. Defined in * android.telephony.TelephonyManger. * @param completionWindowMillis milliseconds in which data throttling action has to be * achieved. */ public void setDataThrottling(Message result, WorkSource workSource, int dataThrottlingAction, long completionWindowMillis) { mCi.setDataThrottling(result, workSource, dataThrottlingAction, completionWindowMillis); } /** * Set allowed carriers */ public void setAllowedCarriers(CarrierRestrictionRules carrierRestrictionRules, Message response, WorkSource workSource) { mCi.setAllowedCarriers(carrierRestrictionRules, response, workSource); } /** Sets the SignalStrength reporting criteria. */ public void setLinkCapacityReportingCriteria(int[] dlThresholds, int[] ulThresholds, int ran) { // no-op default implementation } /** * Get allowed carriers */ public void getAllowedCarriers(Message response, WorkSource workSource) { mCi.getAllowedCarriers(response, workSource); } /** * Returns the locale based on the carrier properties (such as {@code ro.carrier}) and * SIM preferences. */ public Locale getLocaleFromSimAndCarrierPrefs() { final IccRecords records = mIccRecords.get(); if (records != null && records.getSimLanguage() != null) { return new Locale(records.getSimLanguage()); } return getLocaleFromCarrierProperties(); } public boolean updateCurrentCarrierInProvider() { return false; } @UnsupportedAppUsage public IccSmsInterfaceManager getIccSmsInterfaceManager(){ return null; } protected boolean isMatchGid(String gid) { String gid1 = getGroupIdLevel1(); int gidLength = gid.length(); if (!TextUtils.isEmpty(gid1) && (gid1.length() >= gidLength) && gid1.substring(0, gidLength).equalsIgnoreCase(gid)) { return true; } return false; } public static void checkWfcWifiOnlyModeBeforeDial(Phone imsPhone, int phoneId, Context context) throws CallStateException { if (imsPhone == null || !imsPhone.isWifiCallingEnabled()) { ImsManager imsManager = ImsManager.getInstance(context, phoneId); boolean wfcWiFiOnly = (imsManager.isWfcEnabledByPlatform() && imsManager.isWfcEnabledByUser() && (imsManager.getWfcMode() == ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY)); if (wfcWiFiOnly) { throw new CallStateException( CallStateException.ERROR_OUT_OF_SERVICE, "WFC Wi-Fi Only Mode: IMS not registered"); } } } public boolean isImsServiceSimultaneousCallingSupportCapable(Context context) { if (!mFeatureFlags.simultaneousCallingIndications()) return false; boolean capable = false; ImsManager imsManager = ImsManager.getInstance(context, mPhoneId); if (imsManager != null) { try { capable = imsManager.isCapable(CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING); } catch (ImsException e) { loge("initializeTerminalBasedCallWaiting : exception " + e); } } return capable; } public void startRingbackTone() { } public void stopRingbackTone() { } public void callEndCleanupHandOverCallIfAny() { } /** * Cancel USSD session. * * @param msg The message to dispatch when the USSD session terminated. */ public void cancelUSSD(Message msg) { } /** * Set boolean broadcastEmergencyCallStateChanges */ public abstract void setBroadcastEmergencyCallStateChanges(boolean broadcast); public abstract void sendEmergencyCallStateChange(boolean callActive); /** * This function returns the parent phone of the current phone. It is applicable * only for IMS phone (function is overridden by ImsPhone). For others the phone * object itself is returned. * @return */ public Phone getDefaultPhone() { return this; } /** * SIP URIs aliased to the current subscriber given by the IMS implementation. * Applicable only on IMS; used in absence of line1number. * @return array of SIP URIs aliased to the current subscriber */ public Uri[] getCurrentSubscriberUris() { return null; } public AppSmsManager getAppSmsManager() { return mAppSmsManager; } /** * Set SIM card power state. * @param state State of SIM (power down, power up, pass through) * - {@link android.telephony.TelephonyManager#CARD_POWER_DOWN} * - {@link android.telephony.TelephonyManager#CARD_POWER_UP} * - {@link android.telephony.TelephonyManager#CARD_POWER_UP_PASS_THROUGH} **/ public void setSimPowerState(int state, Message result, WorkSource workSource) { mCi.setSimCardPower(state, result, workSource); } /** * Enable or disable Voice over NR (VoNR) * @param enabled enable or disable VoNR. **/ public void setVoNrEnabled(boolean enabled, Message result, WorkSource workSource) { mCi.setVoNrEnabled(enabled, result, workSource); } /** * Is voice over NR enabled */ public void isVoNrEnabled(Message message, WorkSource workSource) { mCi.isVoNrEnabled(message, workSource); } public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1, String gid2, String pnn, String spn, String carrierPrivilegeRules, String apn) { } /** * Check if the device can only make the emergency call. The device is emergency call only if * none of the phone is in service, and one of them has the capability to make the emergency * call. * * @return {@code True} if the device is emergency call only, otherwise return {@code False}. */ public static boolean isEmergencyCallOnly() { boolean isEmergencyCallOnly = false; for (Phone phone : PhoneFactory.getPhones()) { if (phone != null) { ServiceStateTracker sst = phone.getServiceStateTracker(); ServiceState ss = sst.getServiceState(); // Combined reg state is in service, hence the device is not emergency call only. if (sst.getCombinedRegState(ss) == ServiceState.STATE_IN_SERVICE) { return false; } isEmergencyCallOnly |= ss.isEmergencyOnly(); } } return isEmergencyCallOnly; } // Return true if either CSIM or RUIM app is present. By default it returns false. public boolean isCdmaSubscriptionAppPresent() { return false; } /** * Enable or disable uicc applications. * @param enable whether to enable or disable uicc applications. * @param onCompleteMessage callback for async operation. Ignored if blockingCall is true. */ public void enableUiccApplications(boolean enable, Message onCompleteMessage) {} /** * Whether disabling a physical subscription is supported or not. */ public boolean canDisablePhysicalSubscription() { return false; } /** * Get the HAL version. * * @return the current HalVersion * * @deprecated Use {@link #getHalVersion(int service)} instead. */ @Deprecated public HalVersion getHalVersion() { return getHalVersion(HAL_SERVICE_RADIO); } /** * Get the HAL version with a specific service. * * @param service the service id to query * @return the current HalVersion for a specific service * */ public HalVersion getHalVersion(@HalService int service) { if (mCi != null && mCi instanceof RIL) { return ((RIL) mCi).getHalVersion(service); } return RIL.RADIO_HAL_VERSION_UNKNOWN; } /** * Get the SIM's MCC/MNC * * @return MCC/MNC in string format, empty string if not available. */ @NonNull public String getOperatorNumeric() { return ""; } /** Returns the {@link VoiceCallSessionStats} for this phone ID. */ public VoiceCallSessionStats getVoiceCallSessionStats() { return mVoiceCallSessionStats; } /** Sets the {@link VoiceCallSessionStats} mock for this phone ID during unit testing. */ @VisibleForTesting public void setVoiceCallSessionStats(VoiceCallSessionStats voiceCallSessionStats) { mVoiceCallSessionStats = voiceCallSessionStats; } /** Returns the {@link SmsStats} for this phone ID. */ public SmsStats getSmsStats() { return mSmsStats; } /** Sets the {@link SmsStats} mock for this phone ID during unit testing. */ @VisibleForTesting public void setSmsStats(SmsStats smsStats) { mSmsStats = smsStats; } /** Getter for Telephony Analytics */ public TelephonyAnalytics getTelephonyAnalytics() { return mTelephonyAnalytics; } /** @hide */ public CarrierPrivilegesTracker getCarrierPrivilegesTracker() { return null; } public boolean useSsOverIms(Message onComplete) { return false; } /** * Check if device is idle. Device is idle when it is not in high power consumption mode. * * @see DeviceStateMonitor#shouldEnableHighPowerConsumptionIndications() * * @return true if device is idle */ public boolean isDeviceIdle() { DeviceStateMonitor dsm = getDeviceStateMonitor(); if (dsm == null) { Rlog.e(LOG_TAG, "isDeviceIdle: DeviceStateMonitor is null"); return false; } return !dsm.shouldEnableHighPowerConsumptionIndications(); } /** * Get notified when device idleness state has changed * * @param isIdle true if the new state is idle */ public void notifyDeviceIdleStateChanged(boolean isIdle) { SignalStrengthController ssc = getSignalStrengthController(); if (ssc == null) { Rlog.e(LOG_TAG, "notifyDeviceIdleStateChanged: SignalStrengthController is null"); return; } ssc.onDeviceIdleStateChanged(isIdle); } /** * Returns a list of the equivalent home PLMNs (EF_EHPLMN) from the USIM app. * * @return A list of equivalent home PLMNs. Returns an empty list if EF_EHPLMN is empty or * does not exist on the SIM card. */ public @NonNull List getEquivalentHomePlmns() { return Collections.emptyList(); } /** * * @return */ public @NonNull List getDataServicePackages() { return Collections.emptyList(); } /** * Return link bandwidth estimator */ public LinkBandwidthEstimator getLinkBandwidthEstimator() { return mLinkBandwidthEstimator; } /** * Request to get the current slicing configuration including URSP rules and * NSSAIs (configured, allowed and rejected). */ public void getSlicingConfig(Message response) { mCi.getSlicingConfig(response); } /** * Returns the InboundSmsHandler object for this phone */ public InboundSmsHandler getInboundSmsHandler(boolean is3gpp2) { return null; } /** * @return The data network controller */ public @Nullable DataNetworkController getDataNetworkController() { return mDataNetworkController; } /** * @return The data settings manager */ public @Nullable DataSettingsManager getDataSettingsManager() { if (mDataNetworkController == null) return null; return mDataNetworkController.getDataSettingsManager(); } /** * Used in unit tests to set whether the AllowedNetworkTypes is loaded from Db. Should not * be used otherwise. * * @return {@code true} if the AllowedNetworkTypes is loaded from Db, * {@code false} otherwise. */ @VisibleForTesting public boolean isAllowedNetworkTypesLoadedFromDb() { return mIsAllowedNetworkTypesLoadedFromDb; } /** * Returns the user's last setting for terminal-based call waiting * @param forCsOnly indicates the caller expects the result for CS calls only */ public int getTerminalBasedCallWaitingState(boolean forCsOnly) { return CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED; } /** * Notifies the change of the user setting of the terminal-based call waiting service * to IMS service. */ public void setTerminalBasedCallWaitingStatus(int state) { } /** * Notifies that the IMS service connected supports the terminal-based call waiting service */ public void setTerminalBasedCallWaitingSupported(boolean supported) { } /** * Notifies the NAS and RRC layers of the radio the type of upcoming IMS traffic. * * @param token A nonce to identify the request. * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc. * @param accessNetworkType The type of the radio access network used. * @param trafficDirection Indicates whether traffic is originated by mobile originated or * mobile terminated use case eg. MO/MT call/SMS etc. * @param response is callback message. */ public void startImsTraffic(int token, @MmTelFeature.ImsTrafficType int trafficType, @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType, @MmTelFeature.ImsTrafficDirection int trafficDirection, Message response) { mCi.startImsTraffic(token, trafficType, accessNetworkType, trafficDirection, response); } /** * Notifies IMS traffic has been stopped. * * @param token The token assigned by startImsTraffic. * @param response is callback message. */ public void stopImsTraffic(int token, Message response) { mCi.stopImsTraffic(token, response); } /** * Register for notifications of connection setup failure * * @param h Handler for notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForConnectionSetupFailure(Handler h, int what, Object obj) { mCi.registerForConnectionSetupFailure(h, what, obj); } /** * Unregister for notifications of connection setup failure * * @param h Handler to be removed from the registrant list. */ public void unregisterForConnectionSetupFailure(Handler h) { mCi.unregisterForConnectionSetupFailure(h); } /** * Triggers the UE initiated EPS fallback procedure. * * @param reason specifies the reason for EPS fallback. * @param response is callback message. */ public void triggerEpsFallback(@MmTelFeature.EpsFallbackReason int reason, Message response) { mCi.triggerEpsFallback(reason, response); } /** * Notifies the recommended bit rate for the indicated logical channel and direction. * * @param mediaType MediaType is used to identify media stream such as audio or video. * @param direction Direction of this packet stream (e.g. uplink or downlink). * @param bitsPerSecond The recommended bit rate for the UE for a specific logical channel and * a specific direction by NW. */ public void triggerNotifyAnbr(int mediaType, int direction, int bitsPerSecond) { } /** * Sets the emergency mode * * @param emcMode The radio emergency mode type. * @param result Callback message. */ public void setEmergencyMode(@EmergencyConstants.EmergencyMode int emcMode, @Nullable Message result) { mCi.setEmergencyMode(emcMode, result); } /** * Triggers an emergency network scan. * * @param accessNetwork Contains the list of access network types to be prioritized * during emergency scan. The 1st entry has the highest priority. * @param scanType Indicates the type of scans to be performed i.e. limited scan, * full service scan or any scan. * @param result Callback message. */ public void triggerEmergencyNetworkScan( @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetwork, @DomainSelectionService.EmergencyScanType int scanType, @Nullable Message result) { mCi.triggerEmergencyNetworkScan(accessNetwork, scanType, result); } /** * Cancels ongoing emergency network scan * @param resetScan Indicates how the next {@link #triggerEmergencyNetworkScan} should work. * If {@code true}, then the modem shall start the new scan from the beginning, * otherwise the modem shall resume from the last search. * @param result Callback message. */ public void cancelEmergencyNetworkScan(boolean resetScan, @Nullable Message result) { mCi.cancelEmergencyNetworkScan(resetScan, result); } /** * Exits ongoing emergency mode * @param result Callback message. */ public void exitEmergencyMode(@Nullable Message result) { mCi.exitEmergencyMode(result); } /** * Registers for emergency network scan result. * * @param h Handler for notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForEmergencyNetworkScan(@NonNull Handler h, int what, @Nullable Object obj) { mCi.registerForEmergencyNetworkScan(h, what, obj); } /** * Unregisters for emergency network scan result. * * @param h Handler to be removed from the registrant list. */ public void unregisterForEmergencyNetworkScan(@NonNull Handler h) { mCi.unregisterForEmergencyNetworkScan(h); } /** * Notifies that IMS deregistration is triggered. * * @param reason the reason why the deregistration is triggered. */ public void triggerImsDeregistration( @ImsRegistrationImplBase.ImsDeregistrationReason int reason) { if (mImsPhone != null) { mImsPhone.triggerImsDeregistration(reason); } } /** * Registers for the domain selected for emergency calls. * * @param h Handler for notification message. * @param what User-defined message code. * @param obj User object. */ public void registerForEmergencyDomainSelected( @NonNull Handler h, int what, @Nullable Object obj) { } /** * Unregisters for the domain selected for emergency calls. * * @param h Handler to be removed from the registrant list. */ public void unregisterForEmergencyDomainSelected(@NonNull Handler h) { } /** * Notifies the domain selected. * * @param transportType The preferred transport type. */ public void notifyEmergencyDomainSelected( @AccessNetworkConstants.TransportType int transportType) { } /** * @return Telephony tester instance. */ public @Nullable TelephonyTester getTelephonyTester() { return mTelephonyTester; } /** * @return User handle associated with the phone's subscription id. {@code null} if subscription * is invalid or not found. */ @Nullable public UserHandle getUserHandle() { int subId = getSubId(); UserHandle userHandle = null; try { SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class); if (subManager != null) { userHandle = subManager.getSubscriptionUserHandle(subId); } } catch (IllegalArgumentException ex) { loge("getUserHandle: ex=" + ex); } return userHandle; } /** * Checks if the context user is a managed profile. * * Note that this applies specifically to managed profiles. * * @return whether the context user is a managed profile. */ public boolean isManagedProfile() { UserHandle userHandle = getUserHandle(); UserManager userManager = mContext.getSystemService(UserManager.class); if (userHandle == null || userManager == null) return false; return userManager.isManagedProfile(userHandle.getIdentifier()); } /** * @return global null cipher and integrity enabled preference */ public boolean getNullCipherAndIntegrityEnabledPreference() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); return sp.getBoolean(PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED, true); } /** * @return whether or not this Phone interacts with a modem that supports the null cipher * and integrity feature. */ public boolean isNullCipherAndIntegritySupported() { return false; } /** * Override to implement handling of an update to the enablement of the null cipher and * integrity preference. * {@see #PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED} */ public void handleNullCipherEnabledChange() { } /** * @return whether or not this Phone interacts with a modem that supports the cellular * identifier disclosure transparency feature. */ public boolean isIdentifierDisclosureTransparencySupported() { return false; } /** * @return global cellular identifier disclosure transparency enabled preference */ public boolean getIdentifierDisclosureNotificationsPreferenceEnabled() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); return sp.getBoolean(PREF_IDENTIFIER_DISCLOSURE_NOTIFICATIONS_ENABLED, false); } /** * Override to handle an update to the cellular identifier disclosure transparency preference. */ public void handleIdentifierDisclosureNotificationPreferenceChange() { } /** * @return whether this Phone interacts with a modem that supports the null cipher * notification feature. */ public boolean isNullCipherNotificationSupported() { return false; } /** * @return whether the global null cipher notifications preference is enabled. */ public boolean getNullCipherNotificationsPreferenceEnabled() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); return sp.getBoolean(PREF_NULL_CIPHER_NOTIFICATIONS_ENABLED, false); } /** * Override to handle an update to the null cipher notification preference. */ public void handleNullCipherNotificationPreferenceChanged() { } /** * Refresh the safety sources in response to the identified broadcast. */ public void refreshSafetySources(String refreshBroadcastId) { } /** * Notifies the IMS call status to the modem. * * @param imsCallInfo The list of {@link ImsCallInfo}. * @param response A callback to receive the response. */ public void updateImsCallStatus(@NonNull List imsCallInfo, Message response) { mCi.updateImsCallStatus(imsCallInfo, response); } /** * Enables or disables N1 mode (access to 5G core network) in accordance with * 3GPP TS 24.501 4.9. * @param enable {@code true} to enable N1 mode, {@code false} to disable N1 mode. * @param result Callback message to receive the result. */ public void setN1ModeEnabled(boolean enable, Message result) { mCi.setN1ModeEnabled(enable, result); } /** * Check whether N1 mode (access to 5G core network) is enabled or not. * @param result Callback message to receive the result. */ public void isN1ModeEnabled(Message result) { mCi.isN1ModeEnabled(result); } /** * Return current cell broadcast ranges. */ public List getCellBroadcastIdRanges() { return new ArrayList<>(); } /** * Set reception of cell broadcast messages with the list of the given ranges. */ public void setCellBroadcastIdRanges( @NonNull List ranges, Consumer callback) { callback.accept(TelephonyManager.CELL_BROADCAST_RESULT_UNSUPPORTED); } /** * Start callback mode * @param type for callback mode entry. */ public void startCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type) { Rlog.d(LOG_TAG, "startCallbackMode:type=" + type); mNotifier.notifyCallbackModeStarted(this, type); } /** * Stop callback mode * @param type for callback mode exit. * @param reason for stopping callback mode. */ public void stopCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type, @TelephonyManager.EmergencyCallbackModeStopReason int reason) { Rlog.d(LOG_TAG, "stopCallbackMode:type=" + type + ", reason=" + reason); mNotifier.notifyCallbackModeStopped(this, type, reason); } /** * Notify carrier roaming non-terrestrial network mode changed * @param active {@code true} If the device is connected to carrier roaming * non-terrestrial network or was connected within the * {CarrierConfigManager#KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT} * duration, {code false} otherwise. */ public void notifyCarrierRoamingNtnModeChanged(boolean active) { logd("notifyCarrierRoamingNtnModeChanged active:" + active); mNotifier.notifyCarrierRoamingNtnModeChanged(this, active); } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("Phone: subId=" + getSubId()); pw.println(" mPhoneId=" + mPhoneId); pw.println(" mCi=" + mCi); pw.println(" mDnsCheckDisabled=" + mDnsCheckDisabled); pw.println(" mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing); pw.println(" mCallRingContinueToken=" + mCallRingContinueToken); pw.println(" mCallRingDelay=" + mCallRingDelay); pw.println(" mIsVoiceCapable=" + mIsVoiceCapable); pw.println(" mIccRecords=" + mIccRecords.get()); pw.println(" mUiccApplication=" + mUiccApplication.get()); pw.println(" mSmsStorageMonitor=" + mSmsStorageMonitor); pw.println(" mSmsUsageMonitor=" + mSmsUsageMonitor); pw.flush(); pw.println(" mLooper=" + mLooper); pw.println(" mContext=" + mContext); pw.println(" mNotifier=" + mNotifier); pw.println(" mSimulatedRadioControl=" + mSimulatedRadioControl); pw.println(" mUnitTestMode=" + mUnitTestMode); pw.println(" isDnsCheckDisabled()=" + isDnsCheckDisabled()); pw.println(" getUnitTestMode()=" + getUnitTestMode()); pw.println(" getState()=" + getState()); pw.println(" getIccSerialNumber()=" + Rlog.pii(LOG_TAG, getIccSerialNumber())); pw.println(" getIccRecordsLoaded()=" + getIccRecordsLoaded()); pw.println(" getMessageWaitingIndicator()=" + getMessageWaitingIndicator()); pw.println(" getCallForwardingIndicator()=" + getCallForwardingIndicator()); pw.println(" isInEmergencyCall()=" + isInEmergencyCall()); pw.flush(); pw.println(" isInEcm()=" + isInEcm()); pw.println(" getPhoneName()=" + getPhoneName()); pw.println(" getPhoneType()=" + getPhoneType()); pw.println(" getVoiceMessageCount()=" + getVoiceMessageCount()); pw.println(" needsOtaServiceProvisioning=" + needsOtaServiceProvisioning()); pw.println(" isInEmergencySmsMode=" + isInEmergencySmsMode()); pw.println(" isEcmCanceledForEmergency=" + isEcmCanceledForEmergency()); pw.println(" service state=" + getServiceState()); pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); if (mImsPhone != null) { try { mImsPhone.dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); } if (mDataNetworkController != null) { try { mDataNetworkController.dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); } if (getServiceStateTracker() != null) { try { getServiceStateTracker().dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); } if (getEmergencyNumberTracker() != null) { try { getEmergencyNumberTracker().dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); } if (getDisplayInfoController() != null) { try { getDisplayInfoController().dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); } if (mCarrierResolver != null) { try { mCarrierResolver.dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); } if (mCarrierActionAgent != null) { try { mCarrierActionAgent.dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); } if (mCarrierSignalAgent != null) { try { mCarrierSignalAgent.dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); } if (getCallTracker() != null) { try { getCallTracker().dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); } if (mSimActivationTracker != null) { try { mSimActivationTracker.dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); } if (mDeviceStateMonitor != null) { pw.println("DeviceStateMonitor:"); mDeviceStateMonitor.dump(fd, pw, args); pw.println("++++++++++++++++++++++++++++++++"); } if (mSignalStrengthController != null) { pw.println("SignalStrengthController:"); mSignalStrengthController.dump(fd, pw, args); pw.println("++++++++++++++++++++++++++++++++"); } if (mAccessNetworksManager != null) { mAccessNetworksManager.dump(fd, pw, args); } if (mCi != null && mCi instanceof RIL) { try { ((RIL)mCi).dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); } if (getCarrierPrivilegesTracker() != null) { pw.println("CarrierPrivilegesTracker:"); getCarrierPrivilegesTracker().dump(fd, pw, args); pw.println("++++++++++++++++++++++++++++++++"); } if (getLinkBandwidthEstimator() != null) { pw.println("LinkBandwidthEstimator:"); getLinkBandwidthEstimator().dump(fd, pw, args); pw.println("++++++++++++++++++++++++++++++++"); } pw.println("Phone Local Log: "); if (mLocalLog != null) { try { mLocalLog.dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.println("++++++++++++++++++++++++++++++++"); } if (mTelephonyAnalytics != null) { try { mTelephonyAnalytics.dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } } } private void logd(String s) { Rlog.d(LOG_TAG, "[" + mPhoneId + "] " + s); } private void logi(String s) { Rlog.i(LOG_TAG, "[" + mPhoneId + "] " + s); } private void loge(String s) { Rlog.e(LOG_TAG, "[" + mPhoneId + "] " + s); } private static String pii(String s) { return Rlog.pii(LOG_TAG, s); } }