| /* |
| * 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 com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE; |
| import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE; |
| import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE; |
| import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION; |
| import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL; |
| import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL; |
| import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY; |
| import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE; |
| import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY; |
| import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL; |
| import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE; |
| |
| import android.app.ActivityManager; |
| import android.content.BroadcastReceiver; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.SharedPreferences; |
| import android.database.SQLException; |
| import android.net.Uri; |
| import android.os.AsyncResult; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.PersistableBundle; |
| import android.os.PowerManager; |
| import android.os.Registrant; |
| import android.os.RegistrantList; |
| import android.os.ResultReceiver; |
| import android.os.SystemProperties; |
| import android.os.UserHandle; |
| import android.os.WorkSource; |
| import android.preference.PreferenceManager; |
| import android.provider.Settings; |
| import android.provider.Telephony; |
| import android.telecom.VideoProfile; |
| import android.telephony.CarrierConfigManager; |
| import android.telephony.CellLocation; |
| import android.telephony.ImsiEncryptionInfo; |
| import android.telephony.NetworkScanRequest; |
| import android.telephony.PhoneNumberUtils; |
| import android.telephony.Rlog; |
| import android.telephony.ServiceState; |
| import android.telephony.SubscriptionInfo; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyManager; |
| import android.telephony.UssdResponse; |
| import android.telephony.cdma.CdmaCellLocation; |
| import android.text.TextUtils; |
| import android.util.Log; |
| |
| import com.android.ims.ImsManager; |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.telephony.cdma.CdmaMmiCode; |
| import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager; |
| import com.android.internal.telephony.cdma.EriManager; |
| import com.android.internal.telephony.gsm.GsmMmiCode; |
| import com.android.internal.telephony.gsm.SuppServiceNotification; |
| import com.android.internal.telephony.test.SimulatedRadioControl; |
| import com.android.internal.telephony.uicc.IccCardProxy; |
| import com.android.internal.telephony.uicc.IccException; |
| import com.android.internal.telephony.uicc.IccRecords; |
| import com.android.internal.telephony.uicc.IccVmNotSupportedException; |
| import com.android.internal.telephony.uicc.IsimRecords; |
| import com.android.internal.telephony.uicc.IsimUiccRecords; |
| import com.android.internal.telephony.uicc.RuimRecords; |
| import com.android.internal.telephony.uicc.SIMRecords; |
| import com.android.internal.telephony.uicc.UiccCard; |
| import com.android.internal.telephony.uicc.UiccCardApplication; |
| import com.android.internal.telephony.uicc.UiccController; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * {@hide} |
| */ |
| public class GsmCdmaPhone extends Phone { |
| // NOTE that LOG_TAG here is "GsmCdma", which means that log messages |
| // from this file will go into the radio log rather than the main |
| // log. (Use "adb logcat -b radio" to see them.) |
| public static final String LOG_TAG = "GsmCdmaPhone"; |
| private static final boolean DBG = true; |
| private static final boolean VDBG = false; /* STOPSHIP if true */ |
| |
| //GSM |
| // Key used to read/write voice mail number |
| private static final String VM_NUMBER = "vm_number_key"; |
| // Key used to read/write the SIM IMSI used for storing the voice mail |
| private static final String VM_SIM_IMSI = "vm_sim_imsi_key"; |
| /** List of Registrants to receive Supplementary Service Notifications. */ |
| private RegistrantList mSsnRegistrants = new RegistrantList(); |
| |
| //CDMA |
| // Default Emergency Callback Mode exit timer |
| private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000; |
| private static final String VM_NUMBER_CDMA = "vm_number_key_cdma"; |
| public static final int RESTART_ECM_TIMER = 0; // restart Ecm timer |
| public static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer |
| private CdmaSubscriptionSourceManager mCdmaSSM; |
| public int mCdmaSubscriptionSource = CdmaSubscriptionSourceManager.SUBSCRIPTION_SOURCE_UNKNOWN; |
| public EriManager mEriManager; |
| private PowerManager.WakeLock mWakeLock; |
| // mEriFileLoadedRegistrants are informed after the ERI text has been loaded |
| private final RegistrantList mEriFileLoadedRegistrants = new RegistrantList(); |
| // mEcmExitRespRegistrant is informed after the phone has been exited |
| private Registrant mEcmExitRespRegistrant; |
| private String mEsn; |
| private String mMeid; |
| // string to define how the carrier specifies its own ota sp number |
| private String mCarrierOtaSpNumSchema; |
| |
| // A runnable which is used to automatically exit from Ecm after a period of time. |
| private Runnable mExitEcmRunnable = new Runnable() { |
| @Override |
| public void run() { |
| exitEmergencyCallbackMode(); |
| } |
| }; |
| public static final String PROPERTY_CDMA_HOME_OPERATOR_NUMERIC = |
| "ro.cdma.home.operator.numeric"; |
| |
| //CDMALTE |
| /** PHONE_TYPE_CDMA_LTE in addition to RuimRecords needs access to SIMRecords and |
| * IsimUiccRecords |
| */ |
| private SIMRecords mSimRecords; |
| |
| //Common |
| // Instance Variables |
| private IsimUiccRecords mIsimUiccRecords; |
| public GsmCdmaCallTracker mCT; |
| public ServiceStateTracker mSST; |
| private ArrayList <MmiCode> mPendingMMIs = new ArrayList<MmiCode>(); |
| private IccPhoneBookInterfaceManager mIccPhoneBookIntManager; |
| private DeviceStateMonitor mDeviceStateMonitor; |
| // Used for identify the carrier of current subscription |
| private CarrierIdentifier mCarrerIdentifier; |
| |
| private int mPrecisePhoneType; |
| |
| // mEcmTimerResetRegistrants are informed after Ecm timer is canceled or re-started |
| private final RegistrantList mEcmTimerResetRegistrants = new RegistrantList(); |
| |
| private String mImei; |
| private String mImeiSv; |
| private String mVmNumber; |
| |
| // Create Cfu (Call forward unconditional) so that dialing number & |
| // mOnComplete (Message object passed by client) can be packed & |
| // given as a single Cfu object as user data to RIL. |
| private static class Cfu { |
| final String mSetCfNumber; |
| final Message mOnComplete; |
| |
| Cfu(String cfNumber, Message onComplete) { |
| mSetCfNumber = cfNumber; |
| mOnComplete = onComplete; |
| } |
| } |
| |
| private IccSmsInterfaceManager mIccSmsInterfaceManager; |
| private IccCardProxy mIccCardProxy; |
| |
| private boolean mResetModemOnRadioTechnologyChange = false; |
| |
| private int mRilVersion; |
| private boolean mBroadcastEmergencyCallStateChanges = false; |
| private CarrierKeyDownloadManager mCDM; |
| // Constructors |
| |
| public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId, |
| int precisePhoneType, TelephonyComponentFactory telephonyComponentFactory) { |
| this(context, ci, notifier, false, phoneId, precisePhoneType, telephonyComponentFactory); |
| } |
| |
| public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, |
| boolean unitTestMode, int phoneId, int precisePhoneType, |
| TelephonyComponentFactory telephonyComponentFactory) { |
| super(precisePhoneType == PhoneConstants.PHONE_TYPE_GSM ? "GSM" : "CDMA", |
| notifier, context, ci, unitTestMode, phoneId, telephonyComponentFactory); |
| |
| // phone type needs to be set before other initialization as other objects rely on it |
| mPrecisePhoneType = precisePhoneType; |
| initOnce(ci); |
| initRatSpecific(precisePhoneType); |
| // CarrierSignalAgent uses CarrierActionAgent in construction so it needs to be created |
| // after CarrierActionAgent. |
| mCarrierActionAgent = mTelephonyComponentFactory.makeCarrierActionAgent(this); |
| mCarrierSignalAgent = mTelephonyComponentFactory.makeCarrierSignalAgent(this); |
| mSST = mTelephonyComponentFactory.makeServiceStateTracker(this, this.mCi); |
| // DcTracker uses SST so needs to be created after it is instantiated |
| mDcTracker = mTelephonyComponentFactory.makeDcTracker(this); |
| mCarrerIdentifier = mTelephonyComponentFactory.makeCarrierIdentifier(this); |
| |
| mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null); |
| mDeviceStateMonitor = mTelephonyComponentFactory.makeDeviceStateMonitor(this); |
| logd("GsmCdmaPhone: constructor: sub = " + mPhoneId); |
| } |
| |
| private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| Rlog.d(LOG_TAG, "mBroadcastReceiver: action " + intent.getAction()); |
| if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { |
| sendMessage(obtainMessage(EVENT_CARRIER_CONFIG_CHANGED)); |
| } |
| } |
| }; |
| |
| private void initOnce(CommandsInterface ci) { |
| if (ci instanceof SimulatedRadioControl) { |
| mSimulatedRadioControl = (SimulatedRadioControl) ci; |
| } |
| |
| mCT = mTelephonyComponentFactory.makeGsmCdmaCallTracker(this); |
| mIccPhoneBookIntManager = mTelephonyComponentFactory.makeIccPhoneBookInterfaceManager(this); |
| PowerManager pm |
| = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); |
| mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); |
| mIccSmsInterfaceManager = mTelephonyComponentFactory.makeIccSmsInterfaceManager(this); |
| mIccCardProxy = mTelephonyComponentFactory.makeIccCardProxy(mContext, mCi, mPhoneId); |
| |
| mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); |
| mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); |
| mCi.registerForOn(this, EVENT_RADIO_ON, null); |
| mCi.setOnSuppServiceNotification(this, EVENT_SSN, null); |
| |
| //GSM |
| mCi.setOnUSSD(this, EVENT_USSD, null); |
| mCi.setOnSs(this, EVENT_SS, null); |
| |
| //CDMA |
| mCdmaSSM = mTelephonyComponentFactory.getCdmaSubscriptionSourceManagerInstance(mContext, |
| mCi, this, EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null); |
| mEriManager = mTelephonyComponentFactory.makeEriManager(this, mContext, |
| EriManager.ERI_FROM_XML); |
| mCi.setEmergencyCallbackMode(this, EVENT_EMERGENCY_CALLBACK_MODE_ENTER, null); |
| mCi.registerForExitEmergencyCallbackMode(this, EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE, |
| null); |
| mCi.registerForModemReset(this, EVENT_MODEM_RESET, null); |
| // get the string that specifies the carrier OTA Sp number |
| mCarrierOtaSpNumSchema = TelephonyManager.from(mContext).getOtaSpNumberSchemaForPhone( |
| getPhoneId(), ""); |
| |
| mResetModemOnRadioTechnologyChange = SystemProperties.getBoolean( |
| TelephonyProperties.PROPERTY_RESET_ON_RADIO_TECH_CHANGE, false); |
| |
| mCi.registerForRilConnected(this, EVENT_RIL_CONNECTED, null); |
| mCi.registerForVoiceRadioTechChanged(this, EVENT_VOICE_RADIO_TECH_CHANGED, null); |
| mContext.registerReceiver(mBroadcastReceiver, new IntentFilter( |
| CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); |
| mCDM = new CarrierKeyDownloadManager(this); |
| } |
| |
| private void initRatSpecific(int precisePhoneType) { |
| mPendingMMIs.clear(); |
| mIccPhoneBookIntManager.updateIccRecords(null); |
| mEsn = null; |
| mMeid = null; |
| |
| mPrecisePhoneType = precisePhoneType; |
| |
| TelephonyManager tm = TelephonyManager.from(mContext); |
| if (isPhoneTypeGsm()) { |
| mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM); |
| tm.setPhoneType(getPhoneId(), PhoneConstants.PHONE_TYPE_GSM); |
| mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS); |
| } else { |
| mCdmaSubscriptionSource = mCdmaSSM.getCdmaSubscriptionSource(); |
| // This is needed to handle phone process crashes |
| mIsPhoneInEcmState = getInEcmMode(); |
| if (mIsPhoneInEcmState) { |
| // Send a message which will invoke handleExitEmergencyCallbackMode |
| mCi.exitEmergencyCallbackMode( |
| obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE)); |
| } |
| |
| mCi.setPhoneType(PhoneConstants.PHONE_TYPE_CDMA); |
| tm.setPhoneType(getPhoneId(), PhoneConstants.PHONE_TYPE_CDMA); |
| mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT); |
| // Sets operator properties by retrieving from build-time system property |
| String operatorAlpha = SystemProperties.get("ro.cdma.home.operator.alpha"); |
| String operatorNumeric = SystemProperties.get(PROPERTY_CDMA_HOME_OPERATOR_NUMERIC); |
| logd("init: operatorAlpha='" + operatorAlpha |
| + "' operatorNumeric='" + operatorNumeric + "'"); |
| if (mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP) == |
| null || isPhoneTypeCdmaLte()) { |
| if (!TextUtils.isEmpty(operatorAlpha)) { |
| logd("init: set 'gsm.sim.operator.alpha' to operator='" + operatorAlpha + "'"); |
| tm.setSimOperatorNameForPhone(mPhoneId, operatorAlpha); |
| } |
| if (!TextUtils.isEmpty(operatorNumeric)) { |
| logd("init: set 'gsm.sim.operator.numeric' to operator='" + operatorNumeric + |
| "'"); |
| logd("update icc_operator_numeric=" + operatorNumeric); |
| tm.setSimOperatorNumericForPhone(mPhoneId, operatorNumeric); |
| |
| SubscriptionController.getInstance().setMccMnc(operatorNumeric, getSubId()); |
| // Sets iso country property by retrieving from build-time system property |
| setIsoCountryProperty(operatorNumeric); |
| // Updates MCC MNC device configuration information |
| logd("update mccmnc=" + operatorNumeric); |
| MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false); |
| } |
| } |
| |
| // Sets current entry in the telephony carrier table |
| updateCurrentCarrierInProvider(operatorNumeric); |
| } |
| } |
| |
| //CDMA |
| /** |
| * Sets PROPERTY_ICC_OPERATOR_ISO_COUNTRY property |
| * |
| */ |
| private void setIsoCountryProperty(String operatorNumeric) { |
| TelephonyManager tm = TelephonyManager.from(mContext); |
| if (TextUtils.isEmpty(operatorNumeric)) { |
| logd("setIsoCountryProperty: clear 'gsm.sim.operator.iso-country'"); |
| tm.setSimCountryIsoForPhone(mPhoneId, ""); |
| } else { |
| String iso = ""; |
| try { |
| iso = MccTable.countryCodeForMcc(Integer.parseInt( |
| operatorNumeric.substring(0,3))); |
| } catch (NumberFormatException ex) { |
| Rlog.e(LOG_TAG, "setIsoCountryProperty: countryCodeForMcc error", ex); |
| } catch (StringIndexOutOfBoundsException ex) { |
| Rlog.e(LOG_TAG, "setIsoCountryProperty: countryCodeForMcc error", ex); |
| } |
| |
| logd("setIsoCountryProperty: set 'gsm.sim.operator.iso-country' to iso=" + iso); |
| tm.setSimCountryIsoForPhone(mPhoneId, iso); |
| } |
| } |
| |
| public boolean isPhoneTypeGsm() { |
| return mPrecisePhoneType == PhoneConstants.PHONE_TYPE_GSM; |
| } |
| |
| public boolean isPhoneTypeCdma() { |
| return mPrecisePhoneType == PhoneConstants.PHONE_TYPE_CDMA; |
| } |
| |
| public boolean isPhoneTypeCdmaLte() { |
| return mPrecisePhoneType == PhoneConstants.PHONE_TYPE_CDMA_LTE; |
| } |
| |
| private void switchPhoneType(int precisePhoneType) { |
| removeCallbacks(mExitEcmRunnable); |
| |
| initRatSpecific(precisePhoneType); |
| |
| mSST.updatePhoneType(); |
| setPhoneName(precisePhoneType == PhoneConstants.PHONE_TYPE_GSM ? "GSM" : "CDMA"); |
| onUpdateIccAvailability(); |
| mCT.updatePhoneType(); |
| |
| CommandsInterface.RadioState radioState = mCi.getRadioState(); |
| if (radioState.isAvailable()) { |
| handleRadioAvailable(); |
| if (radioState.isOn()) { |
| handleRadioOn(); |
| } |
| } |
| if (!radioState.isAvailable() || !radioState.isOn()) { |
| handleRadioOffOrNotAvailable(); |
| } |
| } |
| |
| @Override |
| protected void finalize() { |
| if(DBG) logd("GsmCdmaPhone finalized"); |
| if (mWakeLock != null && mWakeLock.isHeld()) { |
| Rlog.e(LOG_TAG, "UNEXPECTED; mWakeLock is held when finalizing."); |
| mWakeLock.release(); |
| } |
| } |
| |
| @Override |
| public ServiceState getServiceState() { |
| if (mSST == null || mSST.mSS.getState() != ServiceState.STATE_IN_SERVICE) { |
| if (mImsPhone != null) { |
| return ServiceState.mergeServiceStates( |
| (mSST == null) ? new ServiceState() : mSST.mSS, |
| mImsPhone.getServiceState()); |
| } |
| } |
| |
| if (mSST != null) { |
| return mSST.mSS; |
| } else { |
| // avoid potential NPE in EmergencyCallHelper during Phone switch |
| return new ServiceState(); |
| } |
| } |
| |
| @Override |
| public CellLocation getCellLocation(WorkSource workSource) { |
| if (isPhoneTypeGsm()) { |
| return mSST.getCellLocation(workSource); |
| } else { |
| CdmaCellLocation loc = (CdmaCellLocation)mSST.mCellLoc; |
| |
| int mode = Settings.Secure.getInt(getContext().getContentResolver(), |
| Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF); |
| if (mode == Settings.Secure.LOCATION_MODE_OFF) { |
| // clear lat/long values for location privacy |
| CdmaCellLocation privateLoc = new CdmaCellLocation(); |
| privateLoc.setCellLocationData(loc.getBaseStationId(), |
| CdmaCellLocation.INVALID_LAT_LONG, |
| CdmaCellLocation.INVALID_LAT_LONG, |
| loc.getSystemId(), loc.getNetworkId()); |
| loc = privateLoc; |
| } |
| return loc; |
| } |
| } |
| |
| @Override |
| public PhoneConstants.State getState() { |
| if (mImsPhone != null) { |
| PhoneConstants.State imsState = mImsPhone.getState(); |
| if (imsState != PhoneConstants.State.IDLE) { |
| return imsState; |
| } |
| } |
| |
| return mCT.mState; |
| } |
| |
| @Override |
| public int getPhoneType() { |
| if (mPrecisePhoneType == PhoneConstants.PHONE_TYPE_GSM) { |
| return PhoneConstants.PHONE_TYPE_GSM; |
| } else { |
| return PhoneConstants.PHONE_TYPE_CDMA; |
| } |
| } |
| |
| @Override |
| public ServiceStateTracker getServiceStateTracker() { |
| return mSST; |
| } |
| |
| @Override |
| public CallTracker getCallTracker() { |
| return mCT; |
| } |
| |
| @Override |
| public void updateVoiceMail() { |
| if (isPhoneTypeGsm()) { |
| int countVoiceMessages = 0; |
| IccRecords r = mIccRecords.get(); |
| if (r != null) { |
| // get voice mail count from SIM |
| countVoiceMessages = r.getVoiceMessageCount(); |
| } |
| if (countVoiceMessages == IccRecords.DEFAULT_VOICE_MESSAGE_COUNT) { |
| countVoiceMessages = getStoredVoiceMessageCount(); |
| } |
| logd("updateVoiceMail countVoiceMessages = " + countVoiceMessages |
| + " subId " + getSubId()); |
| setVoiceMessageCount(countVoiceMessages); |
| } else { |
| setVoiceMessageCount(getStoredVoiceMessageCount()); |
| } |
| } |
| |
| @Override |
| public List<? extends MmiCode> |
| getPendingMmiCodes() { |
| return mPendingMMIs; |
| } |
| |
| @Override |
| public PhoneConstants.DataState getDataConnectionState(String apnType) { |
| PhoneConstants.DataState ret = PhoneConstants.DataState.DISCONNECTED; |
| |
| if (mSST == null) { |
| // Radio Technology Change is ongoning, dispose() and removeReferences() have |
| // already been called |
| |
| ret = PhoneConstants.DataState.DISCONNECTED; |
| } else if (mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE |
| && (isPhoneTypeCdma() || isPhoneTypeCdmaLte() || |
| (isPhoneTypeGsm() && !apnType.equals(PhoneConstants.APN_TYPE_EMERGENCY)))) { |
| // If we're out of service, open TCP sockets may still work |
| // but no data will flow |
| |
| // Emergency APN is available even in Out Of Service |
| // Pass the actual State of EPDN |
| |
| ret = PhoneConstants.DataState.DISCONNECTED; |
| } else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */ |
| switch (mDcTracker.getState(apnType)) { |
| case RETRYING: |
| case FAILED: |
| case IDLE: |
| ret = PhoneConstants.DataState.DISCONNECTED; |
| break; |
| |
| case CONNECTED: |
| case DISCONNECTING: |
| if ( mCT.mState != PhoneConstants.State.IDLE |
| && !mSST.isConcurrentVoiceAndDataAllowed()) { |
| ret = PhoneConstants.DataState.SUSPENDED; |
| } else { |
| ret = PhoneConstants.DataState.CONNECTED; |
| } |
| break; |
| |
| case CONNECTING: |
| case SCANNING: |
| ret = PhoneConstants.DataState.CONNECTING; |
| break; |
| } |
| } |
| |
| logd("getDataConnectionState apnType=" + apnType + " ret=" + ret); |
| return ret; |
| } |
| |
| @Override |
| public DataActivityState getDataActivityState() { |
| DataActivityState ret = DataActivityState.NONE; |
| |
| if (mSST.getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) { |
| switch (mDcTracker.getActivity()) { |
| case DATAIN: |
| ret = DataActivityState.DATAIN; |
| break; |
| |
| case DATAOUT: |
| ret = DataActivityState.DATAOUT; |
| break; |
| |
| case DATAINANDOUT: |
| ret = DataActivityState.DATAINANDOUT; |
| break; |
| |
| case DORMANT: |
| ret = DataActivityState.DORMANT; |
| break; |
| |
| default: |
| ret = DataActivityState.NONE; |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Notify any interested party of a Phone state change |
| * {@link com.android.internal.telephony.PhoneConstants.State} |
| */ |
| public void notifyPhoneStateChanged() { |
| mNotifier.notifyPhoneState(this); |
| } |
| |
| /** |
| * Notify registrants of a change in the call state. This notifies changes in |
| * {@link com.android.internal.telephony.Call.State}. Use this when changes |
| * in the precise call state are needed, else use notifyPhoneStateChanged. |
| */ |
| public void notifyPreciseCallStateChanged() { |
| /* we'd love it if this was package-scoped*/ |
| super.notifyPreciseCallStateChangedP(); |
| } |
| |
| public void notifyNewRingingConnection(Connection c) { |
| super.notifyNewRingingConnectionP(c); |
| } |
| |
| public void notifyDisconnect(Connection cn) { |
| mDisconnectRegistrants.notifyResult(cn); |
| |
| mNotifier.notifyDisconnectCause(cn.getDisconnectCause(), cn.getPreciseDisconnectCause()); |
| } |
| |
| public void notifyUnknownConnection(Connection cn) { |
| super.notifyUnknownConnectionP(cn); |
| } |
| |
| @Override |
| public boolean isInEmergencyCall() { |
| if (isPhoneTypeGsm()) { |
| return false; |
| } else { |
| return mCT.isInEmergencyCall(); |
| } |
| } |
| |
| @Override |
| protected void setIsInEmergencyCall() { |
| if (!isPhoneTypeGsm()) { |
| mCT.setIsInEmergencyCall(); |
| } |
| } |
| |
| //CDMA |
| private void sendEmergencyCallbackModeChange(){ |
| //Send an Intent |
| Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); |
| intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, isInEcm()); |
| SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId()); |
| ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL); |
| if (DBG) logd("sendEmergencyCallbackModeChange"); |
| } |
| |
| @Override |
| public void sendEmergencyCallStateChange(boolean callActive) { |
| if (mBroadcastEmergencyCallStateChanges) { |
| Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED); |
| intent.putExtra(PhoneConstants.PHONE_IN_EMERGENCY_CALL, callActive); |
| SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId()); |
| ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL); |
| if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallStateChange: callActive " + callActive); |
| } |
| } |
| |
| @Override |
| public void setBroadcastEmergencyCallStateChanges(boolean broadcast) { |
| mBroadcastEmergencyCallStateChanges = broadcast; |
| } |
| |
| public void notifySuppServiceFailed(SuppService code) { |
| mSuppServiceFailedRegistrants.notifyResult(code); |
| } |
| |
| public void notifyServiceStateChanged(ServiceState ss) { |
| super.notifyServiceStateChangedP(ss); |
| } |
| |
| public void notifyLocationChanged() { |
| mNotifier.notifyCellLocation(this); |
| } |
| |
| @Override |
| public void notifyCallForwardingIndicator() { |
| mNotifier.notifyCallForwardingChanged(this); |
| } |
| |
| // override for allowing access from other classes of this package |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setSystemProperty(String property, String value) { |
| if (getUnitTestMode()) { |
| return; |
| } |
| if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) { |
| TelephonyManager.setTelephonyProperty(mPhoneId, property, value); |
| } else { |
| super.setSystemProperty(property, value); |
| } |
| } |
| |
| @Override |
| public void registerForSuppServiceNotification( |
| Handler h, int what, Object obj) { |
| mSsnRegistrants.addUnique(h, what, obj); |
| if (mSsnRegistrants.size() == 1) mCi.setSuppServiceNotifications(true, null); |
| } |
| |
| @Override |
| public void unregisterForSuppServiceNotification(Handler h) { |
| mSsnRegistrants.remove(h); |
| if (mSsnRegistrants.size() == 0) mCi.setSuppServiceNotifications(false, null); |
| } |
| |
| @Override |
| public void registerForSimRecordsLoaded(Handler h, int what, Object obj) { |
| mSimRecordsLoadedRegistrants.addUnique(h, what, obj); |
| } |
| |
| @Override |
| public void unregisterForSimRecordsLoaded(Handler h) { |
| mSimRecordsLoadedRegistrants.remove(h); |
| } |
| |
| @Override |
| public void acceptCall(int videoState) throws CallStateException { |
| Phone imsPhone = mImsPhone; |
| if ( imsPhone != null && imsPhone.getRingingCall().isRinging() ) { |
| imsPhone.acceptCall(videoState); |
| } else { |
| mCT.acceptCall(); |
| } |
| } |
| |
| @Override |
| public void rejectCall() throws CallStateException { |
| mCT.rejectCall(); |
| } |
| |
| @Override |
| public void switchHoldingAndActive() throws CallStateException { |
| mCT.switchWaitingOrHoldingAndActive(); |
| } |
| |
| @Override |
| public String getIccSerialNumber() { |
| IccRecords r = mIccRecords.get(); |
| if (!isPhoneTypeGsm() && r == null) { |
| // to get ICCID form SIMRecords because it is on MF. |
| r = mUiccController.getIccRecords(mPhoneId, UiccController.APP_FAM_3GPP); |
| } |
| return (r != null) ? r.getIccId() : null; |
| } |
| |
| @Override |
| public String getFullIccSerialNumber() { |
| IccRecords r = mIccRecords.get(); |
| if (!isPhoneTypeGsm() && r == null) { |
| // to get ICCID form SIMRecords because it is on MF. |
| r = mUiccController.getIccRecords(mPhoneId, UiccController.APP_FAM_3GPP); |
| } |
| return (r != null) ? r.getFullIccId() : null; |
| } |
| |
| @Override |
| public boolean canConference() { |
| if (mImsPhone != null && mImsPhone.canConference()) { |
| return true; |
| } |
| if (isPhoneTypeGsm()) { |
| return mCT.canConference(); |
| } else { |
| loge("canConference: not possible in CDMA"); |
| return false; |
| } |
| } |
| |
| @Override |
| public void conference() { |
| if (mImsPhone != null && mImsPhone.canConference()) { |
| logd("conference() - delegated to IMS phone"); |
| try { |
| mImsPhone.conference(); |
| } catch (CallStateException e) { |
| loge(e.toString()); |
| } |
| return; |
| } |
| if (isPhoneTypeGsm()) { |
| mCT.conference(); |
| } else { |
| // three way calls in CDMA will be handled by feature codes |
| loge("conference: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| loge("enableEnhancedVoicePrivacy: not expected on GSM"); |
| } else { |
| mCi.setPreferredVoicePrivacy(enable, onComplete); |
| } |
| } |
| |
| @Override |
| public void getEnhancedVoicePrivacy(Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| loge("getEnhancedVoicePrivacy: not expected on GSM"); |
| } else { |
| mCi.getPreferredVoicePrivacy(onComplete); |
| } |
| } |
| |
| @Override |
| public void clearDisconnected() { |
| mCT.clearDisconnected(); |
| } |
| |
| @Override |
| public boolean canTransfer() { |
| if (isPhoneTypeGsm()) { |
| return mCT.canTransfer(); |
| } else { |
| loge("canTransfer: not possible in CDMA"); |
| return false; |
| } |
| } |
| |
| @Override |
| public void explicitCallTransfer() { |
| if (isPhoneTypeGsm()) { |
| mCT.explicitCallTransfer(); |
| } else { |
| loge("explicitCallTransfer: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public GsmCdmaCall getForegroundCall() { |
| return mCT.mForegroundCall; |
| } |
| |
| @Override |
| public GsmCdmaCall getBackgroundCall() { |
| return mCT.mBackgroundCall; |
| } |
| |
| @Override |
| public Call getRingingCall() { |
| Phone imsPhone = mImsPhone; |
| // It returns the ringing call of ImsPhone if the ringing call of GSMPhone isn't ringing. |
| // In CallManager.registerPhone(), it always registers ringing call of ImsPhone, because |
| // the ringing call of GSMPhone isn't ringing. Consequently, it can't answer GSM call |
| // successfully by invoking TelephonyManager.answerRingingCall() since the implementation |
| // in PhoneInterfaceManager.answerRingingCallInternal() could not get the correct ringing |
| // call from CallManager. So we check the ringing call state of imsPhone first as |
| // accpetCall() does. |
| if ( imsPhone != null && imsPhone.getRingingCall().isRinging()) { |
| return imsPhone.getRingingCall(); |
| } |
| return mCT.mRingingCall; |
| } |
| |
| private boolean handleCallDeflectionIncallSupplementaryService( |
| String dialString) { |
| if (dialString.length() > 1) { |
| return false; |
| } |
| |
| if (getRingingCall().getState() != GsmCdmaCall.State.IDLE) { |
| if (DBG) logd("MmiCode 0: rejectCall"); |
| try { |
| mCT.rejectCall(); |
| } catch (CallStateException e) { |
| if (DBG) Rlog.d(LOG_TAG, |
| "reject failed", e); |
| notifySuppServiceFailed(Phone.SuppService.REJECT); |
| } |
| } else if (getBackgroundCall().getState() != GsmCdmaCall.State.IDLE) { |
| if (DBG) logd("MmiCode 0: hangupWaitingOrBackground"); |
| mCT.hangupWaitingOrBackground(); |
| } |
| |
| return true; |
| } |
| |
| //GSM |
| private boolean handleCallWaitingIncallSupplementaryService(String dialString) { |
| int len = dialString.length(); |
| |
| if (len > 2) { |
| return false; |
| } |
| |
| GsmCdmaCall call = getForegroundCall(); |
| |
| try { |
| if (len > 1) { |
| char ch = dialString.charAt(1); |
| int callIndex = ch - '0'; |
| |
| if (callIndex >= 1 && callIndex <= GsmCdmaCallTracker.MAX_CONNECTIONS_GSM) { |
| if (DBG) logd("MmiCode 1: hangupConnectionByIndex " + callIndex); |
| mCT.hangupConnectionByIndex(call, callIndex); |
| } |
| } else { |
| if (call.getState() != GsmCdmaCall.State.IDLE) { |
| if (DBG) logd("MmiCode 1: hangup foreground"); |
| //mCT.hangupForegroundResumeBackground(); |
| mCT.hangup(call); |
| } else { |
| if (DBG) logd("MmiCode 1: switchWaitingOrHoldingAndActive"); |
| mCT.switchWaitingOrHoldingAndActive(); |
| } |
| } |
| } catch (CallStateException e) { |
| if (DBG) Rlog.d(LOG_TAG, |
| "hangup failed", e); |
| notifySuppServiceFailed(Phone.SuppService.HANGUP); |
| } |
| |
| return true; |
| } |
| |
| private boolean handleCallHoldIncallSupplementaryService(String dialString) { |
| int len = dialString.length(); |
| |
| if (len > 2) { |
| return false; |
| } |
| |
| GsmCdmaCall call = getForegroundCall(); |
| |
| if (len > 1) { |
| try { |
| char ch = dialString.charAt(1); |
| int callIndex = ch - '0'; |
| GsmCdmaConnection conn = mCT.getConnectionByIndex(call, callIndex); |
| |
| // GsmCdma index starts at 1, up to 5 connections in a call, |
| if (conn != null && callIndex >= 1 && callIndex <= GsmCdmaCallTracker.MAX_CONNECTIONS_GSM) { |
| if (DBG) logd("MmiCode 2: separate call " + callIndex); |
| mCT.separate(conn); |
| } else { |
| if (DBG) logd("separate: invalid call index " + callIndex); |
| notifySuppServiceFailed(Phone.SuppService.SEPARATE); |
| } |
| } catch (CallStateException e) { |
| if (DBG) Rlog.d(LOG_TAG, "separate failed", e); |
| notifySuppServiceFailed(Phone.SuppService.SEPARATE); |
| } |
| } else { |
| try { |
| if (getRingingCall().getState() != GsmCdmaCall.State.IDLE) { |
| if (DBG) logd("MmiCode 2: accept ringing call"); |
| mCT.acceptCall(); |
| } else { |
| if (DBG) logd("MmiCode 2: switchWaitingOrHoldingAndActive"); |
| mCT.switchWaitingOrHoldingAndActive(); |
| } |
| } catch (CallStateException e) { |
| if (DBG) Rlog.d(LOG_TAG, "switch failed", e); |
| notifySuppServiceFailed(Phone.SuppService.SWITCH); |
| } |
| } |
| |
| return true; |
| } |
| |
| private boolean handleMultipartyIncallSupplementaryService(String dialString) { |
| if (dialString.length() > 1) { |
| return false; |
| } |
| |
| if (DBG) logd("MmiCode 3: merge calls"); |
| conference(); |
| return true; |
| } |
| |
| private boolean handleEctIncallSupplementaryService(String dialString) { |
| |
| int len = dialString.length(); |
| |
| if (len != 1) { |
| return false; |
| } |
| |
| if (DBG) logd("MmiCode 4: explicit call transfer"); |
| explicitCallTransfer(); |
| return true; |
| } |
| |
| private boolean handleCcbsIncallSupplementaryService(String dialString) { |
| if (dialString.length() > 1) { |
| return false; |
| } |
| |
| Rlog.i(LOG_TAG, "MmiCode 5: CCBS not supported!"); |
| // Treat it as an "unknown" service. |
| notifySuppServiceFailed(Phone.SuppService.UNKNOWN); |
| return true; |
| } |
| |
| @Override |
| public boolean handleInCallMmiCommands(String dialString) throws CallStateException { |
| if (!isPhoneTypeGsm()) { |
| loge("method handleInCallMmiCommands is NOT supported in CDMA!"); |
| return false; |
| } |
| |
| Phone imsPhone = mImsPhone; |
| if (imsPhone != null |
| && imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) { |
| return imsPhone.handleInCallMmiCommands(dialString); |
| } |
| |
| if (!isInCall()) { |
| return false; |
| } |
| |
| if (TextUtils.isEmpty(dialString)) { |
| return false; |
| } |
| |
| boolean result = false; |
| char ch = dialString.charAt(0); |
| switch (ch) { |
| case '0': |
| result = handleCallDeflectionIncallSupplementaryService(dialString); |
| break; |
| case '1': |
| result = handleCallWaitingIncallSupplementaryService(dialString); |
| break; |
| case '2': |
| result = handleCallHoldIncallSupplementaryService(dialString); |
| break; |
| case '3': |
| result = handleMultipartyIncallSupplementaryService(dialString); |
| break; |
| case '4': |
| result = handleEctIncallSupplementaryService(dialString); |
| break; |
| case '5': |
| result = handleCcbsIncallSupplementaryService(dialString); |
| break; |
| default: |
| break; |
| } |
| |
| return result; |
| } |
| |
| public boolean isInCall() { |
| GsmCdmaCall.State foregroundCallState = getForegroundCall().getState(); |
| GsmCdmaCall.State backgroundCallState = getBackgroundCall().getState(); |
| GsmCdmaCall.State ringingCallState = getRingingCall().getState(); |
| |
| return (foregroundCallState.isAlive() || |
| backgroundCallState.isAlive() || |
| ringingCallState.isAlive()); |
| } |
| |
| @Override |
| public Connection dial(String dialString, int videoState) throws CallStateException { |
| return dial(dialString, null, videoState, null); |
| } |
| |
| @Override |
| public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras) |
| throws CallStateException { |
| if (!isPhoneTypeGsm() && uusInfo != null) { |
| throw new CallStateException("Sending UUS information NOT supported in CDMA!"); |
| } |
| |
| boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(getSubId(), dialString); |
| Phone imsPhone = mImsPhone; |
| |
| CarrierConfigManager configManager = |
| (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| boolean alwaysTryImsForEmergencyCarrierConfig = configManager.getConfigForSubId(getSubId()) |
| .getBoolean(CarrierConfigManager.KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL); |
| |
| boolean useImsForCall = isImsUseEnabled() |
| && imsPhone != null |
| && (imsPhone.isVolteEnabled() || imsPhone.isWifiCallingEnabled() || |
| (imsPhone.isVideoEnabled() && VideoProfile.isVideo(videoState))) |
| && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE); |
| |
| boolean useImsForEmergency = imsPhone != null |
| && isEmergency |
| && alwaysTryImsForEmergencyCarrierConfig |
| && ImsManager.getInstance(mContext, mPhoneId).isNonTtyOrTtyOnVolteEnabled() |
| && imsPhone.isImsAvailable(); |
| |
| String dialPart = PhoneNumberUtils.extractNetworkPortionAlt(PhoneNumberUtils. |
| stripSeparators(dialString)); |
| boolean isUt = (dialPart.startsWith("*") || dialPart.startsWith("#")) |
| && dialPart.endsWith("#"); |
| |
| boolean useImsForUt = imsPhone != null && imsPhone.isUtEnabled(); |
| |
| if (DBG) { |
| logd("useImsForCall=" + useImsForCall |
| + ", useImsForEmergency=" + useImsForEmergency |
| + ", useImsForUt=" + useImsForUt |
| + ", isUt=" + isUt |
| + ", imsPhone=" + imsPhone |
| + ", imsPhone.isVolteEnabled()=" |
| + ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A") |
| + ", imsPhone.isVowifiEnabled()=" |
| + ((imsPhone != null) ? imsPhone.isWifiCallingEnabled() : "N/A") |
| + ", imsPhone.isVideoEnabled()=" |
| + ((imsPhone != null) ? imsPhone.isVideoEnabled() : "N/A") |
| + ", imsPhone.getServiceState().getState()=" |
| + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A")); |
| } |
| |
| Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mPhoneId, mContext); |
| |
| if ((useImsForCall && !isUt) || (isUt && useImsForUt) || useImsForEmergency) { |
| try { |
| if (DBG) logd("Trying IMS PS call"); |
| return imsPhone.dial(dialString, uusInfo, videoState, intentExtras); |
| } catch (CallStateException e) { |
| if (DBG) logd("IMS PS call exception " + e + |
| "useImsForCall =" + useImsForCall + ", imsPhone =" + imsPhone); |
| // Do not throw a CallStateException and instead fall back to Circuit switch |
| // for emergency calls and MMI codes. |
| if (Phone.CS_FALLBACK.equals(e.getMessage()) || isEmergency) { |
| logi("IMS call failed with Exception: " + e.getMessage() + ". Falling back " |
| + "to CS."); |
| } else { |
| CallStateException ce = new CallStateException(e.getMessage()); |
| ce.setStackTrace(e.getStackTrace()); |
| throw ce; |
| } |
| } |
| } |
| |
| if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE |
| && mSST.mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE && !isEmergency) { |
| throw new CallStateException("cannot dial in current state"); |
| } |
| // Check non-emergency voice CS call - shouldn't dial when POWER_OFF |
| if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_POWER_OFF /* CS POWER_OFF */ |
| && !VideoProfile.isVideo(videoState) /* voice call */ |
| && !isEmergency /* non-emergency call */) { |
| throw new CallStateException( |
| CallStateException.ERROR_POWER_OFF, |
| "cannot dial voice call in airplane mode"); |
| } |
| // Check for service before placing non emergency CS voice call. |
| // Allow dial only if either CS is camped on any RAT (or) PS is in LTE service. |
| if (mSST != null |
| && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE /* CS out of service */ |
| && !(mSST.mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE |
| && ServiceState.isLte(mSST.mSS.getRilDataRadioTechnology())) /* PS not in LTE */ |
| && !VideoProfile.isVideo(videoState) /* voice call */ |
| && !isEmergency /* non-emergency call */) { |
| throw new CallStateException( |
| CallStateException.ERROR_OUT_OF_SERVICE, |
| "cannot dial voice call in out of service"); |
| } |
| if (DBG) logd("Trying (non-IMS) CS call"); |
| |
| if (isPhoneTypeGsm()) { |
| return dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, intentExtras); |
| } else { |
| return dialInternal(dialString, null, videoState, intentExtras); |
| } |
| } |
| |
| /** |
| * @return {@code true} if the user should be informed of an attempt to dial an international |
| * number while on WFC only, {@code false} otherwise. |
| */ |
| public boolean isNotificationOfWfcCallRequired(String dialString) { |
| CarrierConfigManager configManager = |
| (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle config = configManager.getConfigForSubId(getSubId()); |
| |
| // Determine if carrier config indicates that international calls over WFC should trigger a |
| // notification to the user. This is controlled by carrier configuration and is off by |
| // default. |
| boolean shouldNotifyInternationalCallOnWfc = config != null |
| && config.getBoolean( |
| CarrierConfigManager.KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL); |
| |
| if (!shouldNotifyInternationalCallOnWfc) { |
| return false; |
| } |
| |
| Phone imsPhone = mImsPhone; |
| boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(getSubId(), dialString); |
| boolean shouldConfirmCall = |
| // Using IMS |
| isImsUseEnabled() |
| && imsPhone != null |
| // VoLTE not available |
| && !imsPhone.isVolteEnabled() |
| // WFC is available |
| && imsPhone.isWifiCallingEnabled() |
| && !isEmergency |
| // Dialing international number |
| && PhoneNumberUtils.isInternationalNumber(dialString, getCountryIso()); |
| return shouldConfirmCall; |
| } |
| |
| @Override |
| protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState, |
| Bundle intentExtras) |
| throws CallStateException { |
| return dialInternal(dialString, uusInfo, videoState, intentExtras, null); |
| } |
| |
| protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState, |
| Bundle intentExtras, ResultReceiver wrappedCallback) |
| throws CallStateException { |
| |
| // Need to make sure dialString gets parsed properly |
| String newDialString = PhoneNumberUtils.stripSeparators(dialString); |
| |
| if (isPhoneTypeGsm()) { |
| // handle in-call MMI first if applicable |
| if (handleInCallMmiCommands(newDialString)) { |
| return null; |
| } |
| |
| // Only look at the Network portion for mmi |
| String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString); |
| GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this, |
| mUiccApplication.get(), wrappedCallback); |
| if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'..."); |
| |
| if (mmi == null) { |
| return mCT.dial(newDialString, uusInfo, intentExtras); |
| } else if (mmi.isTemporaryModeCLIR()) { |
| return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras); |
| } else { |
| mPendingMMIs.add(mmi); |
| mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); |
| mmi.processCode(); |
| return null; |
| } |
| } else { |
| return mCT.dial(newDialString); |
| } |
| } |
| |
| @Override |
| public boolean handlePinMmi(String dialString) { |
| MmiCode mmi; |
| if (isPhoneTypeGsm()) { |
| mmi = GsmMmiCode.newFromDialString(dialString, this, mUiccApplication.get()); |
| } else { |
| mmi = CdmaMmiCode.newFromDialString(dialString, this, mUiccApplication.get()); |
| } |
| |
| if (mmi != null && mmi.isPinPukCommand()) { |
| mPendingMMIs.add(mmi); |
| mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); |
| try { |
| mmi.processCode(); |
| } catch (CallStateException e) { |
| //do nothing |
| } |
| return true; |
| } |
| |
| loge("Mmi is null or unrecognized!"); |
| return false; |
| } |
| |
| private void sendUssdResponse(String ussdRequest, CharSequence message, int returnCode, |
| ResultReceiver wrappedCallback) { |
| UssdResponse response = new UssdResponse(ussdRequest, message); |
| Bundle returnData = new Bundle(); |
| returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response); |
| wrappedCallback.send(returnCode, returnData); |
| } |
| |
| @Override |
| public boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback) { |
| if (!isPhoneTypeGsm() || mPendingMMIs.size() > 0) { |
| //todo: replace the generic failure with specific error code. |
| sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE, |
| wrappedCallback ); |
| return true; |
| } |
| |
| // Try over IMS if possible. |
| Phone imsPhone = mImsPhone; |
| if ((imsPhone != null) |
| && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) |
| || imsPhone.isUtEnabled())) { |
| try { |
| logd("handleUssdRequest: attempting over IMS"); |
| return imsPhone.handleUssdRequest(ussdRequest, wrappedCallback); |
| } catch (CallStateException cse) { |
| if (!CS_FALLBACK.equals(cse.getMessage())) { |
| return false; |
| } |
| // At this point we've tried over IMS but have been informed we need to handover |
| // back to GSM. |
| logd("handleUssdRequest: fallback to CS required"); |
| } |
| } |
| |
| // Try USSD over GSM. |
| try { |
| dialInternal(ussdRequest, null, VideoProfile.STATE_AUDIO_ONLY, null, |
| wrappedCallback); |
| } catch (Exception e) { |
| logd("handleUssdRequest: exception" + e); |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public void sendUssdResponse(String ussdMessge) { |
| if (isPhoneTypeGsm()) { |
| GsmMmiCode mmi = GsmMmiCode.newFromUssdUserInput(ussdMessge, this, mUiccApplication.get()); |
| mPendingMMIs.add(mmi); |
| mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); |
| mmi.sendUssd(ussdMessge); |
| } else { |
| loge("sendUssdResponse: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public void sendDtmf(char c) { |
| if (!PhoneNumberUtils.is12Key(c)) { |
| loge("sendDtmf called with invalid character '" + c + "'"); |
| } else { |
| if (mCT.mState == PhoneConstants.State.OFFHOOK) { |
| mCi.sendDtmf(c, null); |
| } |
| } |
| } |
| |
| @Override |
| public void startDtmf(char c) { |
| if (!PhoneNumberUtils.is12Key(c)) { |
| loge("startDtmf called with invalid character '" + c + "'"); |
| } else { |
| mCi.startDtmf(c, null); |
| } |
| } |
| |
| @Override |
| public void stopDtmf() { |
| mCi.stopDtmf(null); |
| } |
| |
| @Override |
| public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| loge("[GsmCdmaPhone] sendBurstDtmf() is a CDMA method"); |
| } else { |
| boolean check = true; |
| for (int itr = 0;itr < dtmfString.length(); itr++) { |
| if (!PhoneNumberUtils.is12Key(dtmfString.charAt(itr))) { |
| Rlog.e(LOG_TAG, |
| "sendDtmf called with invalid character '" + dtmfString.charAt(itr)+ "'"); |
| check = false; |
| break; |
| } |
| } |
| if (mCT.mState == PhoneConstants.State.OFFHOOK && check) { |
| mCi.sendBurstDtmf(dtmfString, on, off, onComplete); |
| } |
| } |
| } |
| |
| @Override |
| public void setRadioPower(boolean power) { |
| mSST.setRadioPower(power); |
| } |
| |
| private void storeVoiceMailNumber(String number) { |
| SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); |
| SharedPreferences.Editor editor = sp.edit(); |
| if (isPhoneTypeGsm()) { |
| editor.putString(VM_NUMBER + getPhoneId(), number); |
| editor.apply(); |
| setVmSimImsi(getSubscriberId()); |
| } else { |
| editor.putString(VM_NUMBER_CDMA + getPhoneId(), number); |
| editor.apply(); |
| } |
| } |
| |
| @Override |
| public String getVoiceMailNumber() { |
| String number = null; |
| if (isPhoneTypeGsm()) { |
| // Read from the SIM. If its null, try reading from the shared preference area. |
| IccRecords r = mIccRecords.get(); |
| number = (r != null) ? r.getVoiceMailNumber() : ""; |
| if (TextUtils.isEmpty(number)) { |
| SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); |
| number = sp.getString(VM_NUMBER + getPhoneId(), null); |
| } |
| } else { |
| SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); |
| number = sp.getString(VM_NUMBER_CDMA + getPhoneId(), null); |
| } |
| |
| if (TextUtils.isEmpty(number)) { |
| CarrierConfigManager configManager = (CarrierConfigManager) |
| getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle b = configManager.getConfig(); |
| if (b != null) { |
| String defaultVmNumber = |
| b.getString(CarrierConfigManager.KEY_DEFAULT_VM_NUMBER_STRING); |
| if (!TextUtils.isEmpty(defaultVmNumber)) { |
| number = defaultVmNumber; |
| } |
| } |
| } |
| |
| if (!isPhoneTypeGsm() && TextUtils.isEmpty(number)) { |
| // Read platform settings for dynamic voicemail number |
| CarrierConfigManager configManager = (CarrierConfigManager) |
| getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle b = configManager.getConfig(); |
| if (b != null && b.getBoolean( |
| CarrierConfigManager.KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL)) { |
| number = getLine1Number(); |
| } else { |
| number = "*86"; |
| } |
| } |
| |
| return number; |
| } |
| |
| private String getVmSimImsi() { |
| SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); |
| return sp.getString(VM_SIM_IMSI + getPhoneId(), null); |
| } |
| |
| private void setVmSimImsi(String imsi) { |
| SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); |
| SharedPreferences.Editor editor = sp.edit(); |
| editor.putString(VM_SIM_IMSI + getPhoneId(), imsi); |
| editor.apply(); |
| } |
| |
| @Override |
| public String getVoiceMailAlphaTag() { |
| String ret = ""; |
| |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| |
| ret = (r != null) ? r.getVoiceMailAlphaTag() : ""; |
| } |
| |
| if (ret == null || ret.length() == 0) { |
| return mContext.getText( |
| com.android.internal.R.string.defaultVoiceMailAlphaTag).toString(); |
| } |
| |
| return ret; |
| } |
| |
| @Override |
| public String getDeviceId() { |
| if (isPhoneTypeGsm()) { |
| return mImei; |
| } else { |
| CarrierConfigManager configManager = (CarrierConfigManager) |
| mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| boolean force_imei = configManager.getConfigForSubId(getSubId()) |
| .getBoolean(CarrierConfigManager.KEY_FORCE_IMEI_BOOL); |
| if (force_imei) return mImei; |
| |
| String id = getMeid(); |
| if ((id == null) || id.matches("^0*$")) { |
| loge("getDeviceId(): MEID is not initialized use ESN"); |
| id = getEsn(); |
| } |
| return id; |
| } |
| } |
| |
| @Override |
| public String getDeviceSvn() { |
| if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) { |
| return mImeiSv; |
| } else { |
| loge("getDeviceSvn(): return 0"); |
| return "0"; |
| } |
| } |
| |
| @Override |
| public IsimRecords getIsimRecords() { |
| return mIsimUiccRecords; |
| } |
| |
| @Override |
| public String getImei() { |
| return mImei; |
| } |
| |
| @Override |
| public String getEsn() { |
| if (isPhoneTypeGsm()) { |
| loge("[GsmCdmaPhone] getEsn() is a CDMA method"); |
| return "0"; |
| } else { |
| return mEsn; |
| } |
| } |
| |
| @Override |
| public String getMeid() { |
| return mMeid; |
| } |
| |
| @Override |
| public String getNai() { |
| IccRecords r = mUiccController.getIccRecords(mPhoneId, UiccController.APP_FAM_3GPP2); |
| if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { |
| Rlog.v(LOG_TAG, "IccRecords is " + r); |
| } |
| return (r != null) ? r.getNAI() : null; |
| } |
| |
| @Override |
| public String getSubscriberId() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.getIMSI() : null; |
| } else if (isPhoneTypeCdma()) { |
| return mSST.getImsi(); |
| } else { //isPhoneTypeCdmaLte() |
| return (mSimRecords != null) ? mSimRecords.getIMSI() : ""; |
| } |
| } |
| |
| @Override |
| public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) { |
| return CarrierInfoManager.getCarrierInfoForImsiEncryption(keyType, mContext); |
| } |
| |
| @Override |
| public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) { |
| CarrierInfoManager.setCarrierInfoForImsiEncryption(imsiEncryptionInfo, mContext); |
| } |
| |
| @Override |
| public int getCarrierId() { |
| return mCarrerIdentifier.getCarrierId(); |
| } |
| |
| @Override |
| public String getCarrierName() { |
| return mCarrerIdentifier.getCarrierName(); |
| } |
| |
| @Override |
| public String getGroupIdLevel1() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.getGid1() : null; |
| } else if (isPhoneTypeCdma()) { |
| loge("GID1 is not available in CDMA"); |
| return null; |
| } else { //isPhoneTypeCdmaLte() |
| return (mSimRecords != null) ? mSimRecords.getGid1() : ""; |
| } |
| } |
| |
| @Override |
| public String getGroupIdLevel2() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.getGid2() : null; |
| } else if (isPhoneTypeCdma()) { |
| loge("GID2 is not available in CDMA"); |
| return null; |
| } else { //isPhoneTypeCdmaLte() |
| return (mSimRecords != null) ? mSimRecords.getGid2() : ""; |
| } |
| } |
| |
| @Override |
| public String getLine1Number() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.getMsisdnNumber() : null; |
| } else { |
| return mSST.getMdnNumber(); |
| } |
| } |
| |
| @Override |
| public String getPlmn() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.getPnnHomeName() : null; |
| } else if (isPhoneTypeCdma()) { |
| loge("Plmn is not available in CDMA"); |
| return null; |
| } else { //isPhoneTypeCdmaLte() |
| return (mSimRecords != null) ? mSimRecords.getPnnHomeName() : null; |
| } |
| } |
| |
| @Override |
| public String getCdmaPrlVersion() { |
| return mSST.getPrlVersion(); |
| } |
| |
| @Override |
| public String getCdmaMin() { |
| return mSST.getCdmaMin(); |
| } |
| |
| @Override |
| public boolean isMinInfoReady() { |
| return mSST.isMinInfoReady(); |
| } |
| |
| @Override |
| public String getMsisdn() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.getMsisdnNumber() : null; |
| } else if (isPhoneTypeCdmaLte()) { |
| return (mSimRecords != null) ? mSimRecords.getMsisdnNumber() : null; |
| } else { |
| loge("getMsisdn: not expected on CDMA"); |
| return null; |
| } |
| } |
| |
| @Override |
| public String getLine1AlphaTag() { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.getMsisdnAlphaTag() : null; |
| } else { |
| loge("getLine1AlphaTag: not possible in CDMA"); |
| return null; |
| } |
| } |
| |
| @Override |
| public boolean setLine1Number(String alphaTag, String number, Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| if (r != null) { |
| r.setMsisdnNumber(alphaTag, number, onComplete); |
| return true; |
| } else { |
| return false; |
| } |
| } else { |
| loge("setLine1Number: not possible in CDMA"); |
| return false; |
| } |
| } |
| |
| @Override |
| public void setVoiceMailNumber(String alphaTag, String voiceMailNumber, Message onComplete) { |
| Message resp; |
| mVmNumber = voiceMailNumber; |
| resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete); |
| IccRecords r = mIccRecords.get(); |
| if (r != null) { |
| r.setVoiceMailNumber(alphaTag, mVmNumber, resp); |
| } |
| } |
| |
| private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) { |
| switch (commandInterfaceCFReason) { |
| case CF_REASON_UNCONDITIONAL: |
| case CF_REASON_BUSY: |
| case CF_REASON_NO_REPLY: |
| case CF_REASON_NOT_REACHABLE: |
| case CF_REASON_ALL: |
| case CF_REASON_ALL_CONDITIONAL: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| @Override |
| public String getSystemProperty(String property, String defValue) { |
| if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) { |
| if (getUnitTestMode()) { |
| return null; |
| } |
| return TelephonyManager.getTelephonyProperty(mPhoneId, property, defValue); |
| } else { |
| return super.getSystemProperty(property, defValue); |
| } |
| } |
| |
| private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) { |
| switch (commandInterfaceCFAction) { |
| case CF_ACTION_DISABLE: |
| case CF_ACTION_ENABLE: |
| case CF_ACTION_REGISTRATION: |
| case CF_ACTION_ERASURE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| private boolean isCfEnable(int action) { |
| return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION); |
| } |
| |
| @Override |
| public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| Phone imsPhone = mImsPhone; |
| if ((imsPhone != null) |
| && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) |
| || imsPhone.isUtEnabled())) { |
| imsPhone.getCallForwardingOption(commandInterfaceCFReason, onComplete); |
| return; |
| } |
| |
| if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) { |
| if (DBG) logd("requesting call forwarding query."); |
| Message resp; |
| if (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL) { |
| resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete); |
| } else { |
| resp = onComplete; |
| } |
| mCi.queryCallForwardStatus(commandInterfaceCFReason, 0, null, resp); |
| } |
| } else { |
| loge("getCallForwardingOption: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public void setCallForwardingOption(int commandInterfaceCFAction, |
| int commandInterfaceCFReason, |
| String dialingNumber, |
| int timerSeconds, |
| Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| Phone imsPhone = mImsPhone; |
| if ((imsPhone != null) |
| && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) |
| || imsPhone.isUtEnabled())) { |
| imsPhone.setCallForwardingOption(commandInterfaceCFAction, |
| commandInterfaceCFReason, dialingNumber, timerSeconds, onComplete); |
| return; |
| } |
| |
| if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) && |
| (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) { |
| |
| Message resp; |
| if (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL) { |
| Cfu cfu = new Cfu(dialingNumber, onComplete); |
| resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE, |
| isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, cfu); |
| } else { |
| resp = onComplete; |
| } |
| mCi.setCallForward(commandInterfaceCFAction, |
| commandInterfaceCFReason, |
| CommandsInterface.SERVICE_CLASS_VOICE, |
| dialingNumber, |
| timerSeconds, |
| resp); |
| } |
| } else { |
| loge("setCallForwardingOption: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public void getOutgoingCallerIdDisplay(Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| Phone imsPhone = mImsPhone; |
| if ((imsPhone != null) |
| && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) |
| || imsPhone.isUtEnabled())) { |
| imsPhone.getOutgoingCallerIdDisplay(onComplete); |
| return; |
| } |
| mCi.getCLIR(onComplete); |
| } else { |
| loge("getOutgoingCallerIdDisplay: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| Phone imsPhone = mImsPhone; |
| if ((imsPhone != null) |
| && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) |
| || imsPhone.isUtEnabled())) { |
| imsPhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode, onComplete); |
| return; |
| } |
| // Packing CLIR value in the message. This will be required for |
| // SharedPreference caching, if the message comes back as part of |
| // a success response. |
| mCi.setCLIR(commandInterfaceCLIRMode, |
| obtainMessage(EVENT_SET_CLIR_COMPLETE, commandInterfaceCLIRMode, 0, onComplete)); |
| } else { |
| loge("setOutgoingCallerIdDisplay: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public void getCallWaiting(Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| Phone imsPhone = mImsPhone; |
| if ((imsPhone != null) |
| && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) |
| || imsPhone.isUtEnabled())) { |
| imsPhone.getCallWaiting(onComplete); |
| return; |
| } |
| |
| //As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service |
| //class parameter in call waiting interrogation to network |
| mCi.queryCallWaiting(CommandsInterface.SERVICE_CLASS_NONE, onComplete); |
| } else { |
| mCi.queryCallWaiting(CommandsInterface.SERVICE_CLASS_VOICE, onComplete); |
| } |
| } |
| |
| @Override |
| public void setCallWaiting(boolean enable, Message onComplete) { |
| if (isPhoneTypeGsm()) { |
| Phone imsPhone = mImsPhone; |
| if ((imsPhone != null) |
| && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) |
| || imsPhone.isUtEnabled())) { |
| imsPhone.setCallWaiting(enable, onComplete); |
| return; |
| } |
| |
| mCi.setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete); |
| } else { |
| loge("method setCallWaiting is NOT supported in CDMA!"); |
| } |
| } |
| |
| @Override |
| public void getAvailableNetworks(Message response) { |
| if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) { |
| mCi.getAvailableNetworks(response); |
| } else { |
| loge("getAvailableNetworks: not possible in CDMA"); |
| } |
| } |
| |
| @Override |
| public void startNetworkScan(NetworkScanRequest nsr, Message response) { |
| mCi.startNetworkScan(nsr, response); |
| } |
| |
| @Override |
| public void stopNetworkScan(Message response) { |
| mCi.stopNetworkScan(response); |
| } |
| |
| @Override |
| public void getNeighboringCids(Message response, WorkSource workSource) { |
| if (isPhoneTypeGsm()) { |
| mCi.getNeighboringCids(response, workSource); |
| } else { |
| /* |
| * This is currently not implemented. At least as of June |
| * 2009, there is no neighbor cell information available for |
| * CDMA because some party is resisting making this |
| * information readily available. Consequently, calling this |
| * function can have no useful effect. This situation may |
| * (and hopefully will) change in the future. |
| */ |
| if (response != null) { |
| CommandException ce = new CommandException( |
| CommandException.Error.REQUEST_NOT_SUPPORTED); |
| AsyncResult.forMessage(response).exception = ce; |
| response.sendToTarget(); |
| } |
| } |
| } |
| |
| @Override |
| public void setTTYMode(int ttyMode, Message onComplete) { |
| // Send out the TTY Mode change over RIL as well |
| super.setTTYMode(ttyMode, onComplete); |
| if (mImsPhone != null) { |
| mImsPhone.setTTYMode(ttyMode, onComplete); |
| } |
| } |
| |
| @Override |
| public void setUiTTYMode(int uiTtyMode, Message onComplete) { |
| if (mImsPhone != null) { |
| mImsPhone.setUiTTYMode(uiTtyMode, onComplete); |
| } |
| } |
| |
| @Override |
| public void setMute(boolean muted) { |
| mCT.setMute(muted); |
| } |
| |
| @Override |
| public boolean getMute() { |
| return mCT.getMute(); |
| } |
| |
| @Override |
| public void getDataCallList(Message response) { |
| mCi.getDataCallList(response); |
| } |
| |
| @Override |
| public void updateServiceLocation() { |
| mSST.enableSingleLocationUpdate(); |
| } |
| |
| @Override |
| public void enableLocationUpdates() { |
| mSST.enableLocationUpdates(); |
| } |
| |
| @Override |
| public void disableLocationUpdates() { |
| mSST.disableLocationUpdates(); |
| } |
| |
| @Override |
| public boolean getDataRoamingEnabled() { |
| return mDcTracker.getDataRoamingEnabled(); |
| } |
| |
| @Override |
| public void setDataRoamingEnabled(boolean enable) { |
| mDcTracker.setDataRoamingEnabledByUser(enable); |
| } |
| |
| @Override |
| public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) { |
| mCi.registerForCdmaOtaProvision(h, what, obj); |
| } |
| |
| @Override |
| public void unregisterForCdmaOtaStatusChange(Handler h) { |
| mCi.unregisterForCdmaOtaProvision(h); |
| } |
| |
| @Override |
| public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) { |
| mSST.registerForSubscriptionInfoReady(h, what, obj); |
| } |
| |
| @Override |
| public void unregisterForSubscriptionInfoReady(Handler h) { |
| mSST.unregisterForSubscriptionInfoReady(h); |
| } |
| |
| @Override |
| public void setOnEcbModeExitResponse(Handler h, int what, Object obj) { |
| mEcmExitRespRegistrant = new Registrant(h, what, obj); |
| } |
| |
| @Override |
| public void unsetOnEcbModeExitResponse(Handler h) { |
| mEcmExitRespRegistrant.clear(); |
| } |
| |
| @Override |
| public void registerForCallWaiting(Handler h, int what, Object obj) { |
| mCT.registerForCallWaiting(h, what, obj); |
| } |
| |
| @Override |
| public void unregisterForCallWaiting(Handler h) { |
| mCT.unregisterForCallWaiting(h); |
| } |
| |
| @Override |
| public boolean isUserDataEnabled() { |
| return mDcTracker.isUserDataEnabled(); |
| } |
| |
| @Override |
| public boolean isDataEnabled() { |
| return mDcTracker.isDataEnabled(); |
| } |
| |
| @Override |
| public void setUserDataEnabled(boolean enable) { |
| mDcTracker.setUserDataEnabled(enable); |
| } |
| |
| /** |
| * Removes the given MMI from the pending list and notifies |
| * registrants that it is complete. |
| * @param mmi MMI that is done |
| */ |
| public void onMMIDone(MmiCode mmi) { |
| |
| /* Only notify complete if it's on the pending list. |
| * Otherwise, it's already been handled (eg, previously canceled). |
| * The exception is cancellation of an incoming USSD-REQUEST, which is |
| * not on the list. |
| */ |
| if (mPendingMMIs.remove(mmi) || (isPhoneTypeGsm() && (mmi.isUssdRequest() || |
| ((GsmMmiCode)mmi).isSsInfo()))) { |
| |
| ResultReceiver receiverCallback = mmi.getUssdCallbackReceiver(); |
| if (receiverCallback != null) { |
| Rlog.i(LOG_TAG, "onMMIDone: invoking callback: " + mmi); |
| int returnCode = (mmi.getState() == MmiCode.State.COMPLETE) ? |
| TelephonyManager.USSD_RETURN_SUCCESS : TelephonyManager.USSD_RETURN_FAILURE; |
| sendUssdResponse(mmi.getDialString(), mmi.getMessage(), returnCode, |
| receiverCallback ); |
| } else { |
| Rlog.i(LOG_TAG, "onMMIDone: notifying registrants: " + mmi); |
| mMmiCompleteRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); |
| } |
| } else { |
| Rlog.i(LOG_TAG, "onMMIDone: invalid response or already handled; ignoring: " + mmi); |
| } |
| } |
| |
| public boolean supports3gppCallForwardingWhileRoaming() { |
| CarrierConfigManager configManager = (CarrierConfigManager) |
| getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle b = configManager.getConfig(); |
| if (b != null) { |
| return b.getBoolean( |
| CarrierConfigManager.KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true); |
| } else { |
| // Default value set in CarrierConfigManager |
| return true; |
| } |
| } |
| |
| private void onNetworkInitiatedUssd(MmiCode mmi) { |
| Rlog.v(LOG_TAG, "onNetworkInitiatedUssd: mmi=" + mmi); |
| mMmiCompleteRegistrants.notifyRegistrants( |
| new AsyncResult(null, mmi, null)); |
| } |
| |
| /** ussdMode is one of CommandsInterface.USSD_MODE_* */ |
| private void onIncomingUSSD (int ussdMode, String ussdMessage) { |
| if (!isPhoneTypeGsm()) { |
| loge("onIncomingUSSD: not expected on GSM"); |
| } |
| boolean isUssdError; |
| boolean isUssdRequest; |
| boolean isUssdRelease; |
| |
| isUssdRequest |
| = (ussdMode == CommandsInterface.USSD_MODE_REQUEST); |
| |
| isUssdError |
| = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY |
| && ussdMode != CommandsInterface.USSD_MODE_REQUEST); |
| |
| isUssdRelease = (ussdMode == CommandsInterface.USSD_MODE_NW_RELEASE); |
| |
| |
| // See comments in GsmMmiCode.java |
| // USSD requests aren't finished until one |
| // of these two events happen |
| GsmMmiCode found = null; |
| for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) { |
| if(((GsmMmiCode)mPendingMMIs.get(i)).isPendingUSSD()) { |
| found = (GsmMmiCode)mPendingMMIs.get(i); |
| break; |
| } |
| } |
| |
| if (found != null) { |
| // Complete pending USSD |
| |
| if (isUssdRelease) { |
| found.onUssdRelease(); |
| } else if (isUssdError) { |
| found.onUssdFinishedError(); |
| } else { |
| found.onUssdFinished(ussdMessage, isUssdRequest); |
| } |
| } else if (!isUssdError && ussdMessage != null) { |
| // pending USSD not found |
| // The network may initiate its own USSD request |
| |
| // ignore everything that isnt a Notify or a Request |
| // also, discard if there is no message to present |
| GsmMmiCode mmi; |
| mmi = GsmMmiCode.newNetworkInitiatedUssd(ussdMessage, |
| isUssdRequest, |
| GsmCdmaPhone.this, |
| mUiccApplication.get()); |
| onNetworkInitiatedUssd(mmi); |
| } |
| } |
| |
| /** |
| * Make sure the network knows our preferred setting. |
| */ |
| private void syncClirSetting() { |
| SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); |
| int clirSetting = sp.getInt(CLIR_KEY + getPhoneId(), -1); |
| Rlog.i(LOG_TAG, "syncClirSetting: " + CLIR_KEY + getPhoneId() + "=" + clirSetting); |
| if (clirSetting >= 0) { |
| mCi.setCLIR(clirSetting, null); |
| } |
| } |
| |
| private void handleRadioAvailable() { |
| mCi.getBasebandVersion(obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE)); |
| |
| mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE)); |
| mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY)); |
| startLceAfterRadioIsAvailable(); |
| } |
| |
| private void handleRadioOn() { |
| /* Proactively query voice radio technologies */ |
| mCi.getVoiceRadioTechnology(obtainMessage(EVENT_REQUEST_VOICE_RADIO_TECH_DONE)); |
| |
| if (!isPhoneTypeGsm()) { |
| mCdmaSubscriptionSource = mCdmaSSM.getCdmaSubscriptionSource(); |
| } |
| |
| // If this is on APM off, SIM may already be loaded. Send setPreferredNetworkType |
| // request to RIL to preserve user setting across APM toggling |
| setPreferredNetworkTypeIfSimLoaded(); |
| } |
| |
| private void handleRadioOffOrNotAvailable() { |
| if (isPhoneTypeGsm()) { |
| // Some MMI requests (eg USSD) are not completed |
| // within the course of a CommandsInterface request |
| // If the radio shuts off or resets while one of these |
| // is pending, we need to clean up. |
| |
| for (int i = mPendingMMIs.size() - 1; i >= 0; i--) { |
| if (((GsmMmiCode) mPendingMMIs.get(i)).isPendingUSSD()) { |
| ((GsmMmiCode) mPendingMMIs.get(i)).onUssdFinishedError(); |
| } |
| } |
| } |
| mRadioOffOrNotAvailableRegistrants.notifyRegistrants(); |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| AsyncResult ar; |
| Message onComplete; |
| |
| switch (msg.what) { |
| case EVENT_RADIO_AVAILABLE: { |
| handleRadioAvailable(); |
| } |
| break; |
| |
| case EVENT_GET_DEVICE_IDENTITY_DONE:{ |
| ar = (AsyncResult)msg.obj; |
| |
| if (ar.exception != null) { |
| break; |
| } |
| String[] respId = (String[])ar.result; |
| mImei = respId[0]; |
| mImeiSv = respId[1]; |
| mEsn = respId[2]; |
| mMeid = respId[3]; |
| } |
| break; |
| |
| case EVENT_EMERGENCY_CALLBACK_MODE_ENTER:{ |
| handleEnterEmergencyCallbackMode(msg); |
| } |
| break; |
| |
| case EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE:{ |
| handleExitEmergencyCallbackMode(msg); |
| } |
| break; |
| |
| case EVENT_MODEM_RESET: { |
| logd("Event EVENT_MODEM_RESET Received" + " isInEcm = " + isInEcm() |
| + " isPhoneTypeGsm = " + isPhoneTypeGsm() + " mImsPhone = " + mImsPhone); |
| if (isInEcm()) { |
| if (isPhoneTypeGsm()) { |
| if (mImsPhone != null) { |
| mImsPhone.handleExitEmergencyCallbackMode(); |
| } |
| } else { |
| handleExitEmergencyCallbackMode(msg); |
| } |
| } |
| } |
| break; |
| |
| case EVENT_RUIM_RECORDS_LOADED: |
| logd("Event EVENT_RUIM_RECORDS_LOADED Received"); |
| updateCurrentCarrierInProvider(); |
| break; |
| |
| case EVENT_RADIO_ON: |
| logd("Event EVENT_RADIO_ON Received"); |
| handleRadioOn(); |
| break; |
| |
| case EVENT_RIL_CONNECTED: |
| ar = (AsyncResult) msg.obj; |
| if (ar.exception == null && ar.result != null) { |
| mRilVersion = (Integer) ar.result; |
| } else { |
| logd("Unexpected exception on EVENT_RIL_CONNECTED"); |
| mRilVersion = -1; |
| } |
| break; |
| |
| case EVENT_VOICE_RADIO_TECH_CHANGED: |
| case EVENT_REQUEST_VOICE_RADIO_TECH_DONE: |
| String what = (msg.what == EVENT_VOICE_RADIO_TECH_CHANGED) ? |
| "EVENT_VOICE_RADIO_TECH_CHANGED" : "EVENT_REQUEST_VOICE_RADIO_TECH_DONE"; |
| ar = (AsyncResult) msg.obj; |
| if (ar.exception == null) { |
| if ((ar.result != null) && (((int[]) ar.result).length != 0)) { |
| int newVoiceTech = ((int[]) ar.result)[0]; |
| logd(what + ": newVoiceTech=" + newVoiceTech); |
| phoneObjectUpdater(newVoiceTech); |
| } else { |
| loge(what + ": has no tech!"); |
| } |
| } else { |
| loge(what + ": exception=" + ar.exception); |
| } |
| break; |
| |
| case EVENT_UPDATE_PHONE_OBJECT: |
| phoneObjectUpdater(msg.arg1); |
| break; |
| |
| case EVENT_CARRIER_CONFIG_CHANGED: |
| // Only check for the voice radio tech if it not going to be updated by the voice |
| // registration changes. |
| if (!mContext.getResources().getBoolean(com.android.internal.R.bool. |
| config_switch_phone_on_voice_reg_state_change)) { |
| mCi.getVoiceRadioTechnology(obtainMessage(EVENT_REQUEST_VOICE_RADIO_TECH_DONE)); |
| } |
| // Force update IMS service |
| ImsManager.getInstance(mContext, mPhoneId).updateImsServiceConfig(true); |
| |
| // Update broadcastEmergencyCallStateChanges |
| CarrierConfigManager configMgr = (CarrierConfigManager) |
| getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle b = configMgr.getConfigForSubId(getSubId()); |
| if (b != null) { |
| boolean broadcastEmergencyCallStateChanges = b.getBoolean( |
| CarrierConfigManager.KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL); |
| logd("broadcastEmergencyCallStateChanges = " + |
| broadcastEmergencyCallStateChanges); |
| setBroadcastEmergencyCallStateChanges(broadcastEmergencyCallStateChanges); |
| } else { |
| loge("didn't get broadcastEmergencyCallStateChanges from carrier config"); |
| } |
| |
| // Changing the cdma roaming settings based carrier config. |
| if (b != null) { |
| int config_cdma_roaming_mode = b.getInt( |
| CarrierConfigManager.KEY_CDMA_ROAMING_MODE_INT); |
| int current_cdma_roaming_mode = |
| Settings.Global.getInt(getContext().getContentResolver(), |
| Settings.Global.CDMA_ROAMING_MODE, |
| TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT); |
| switch (config_cdma_roaming_mode) { |
| // Carrier's cdma_roaming_mode will overwrite the user's previous settings |
| // Keep the user's previous setting in global variable which will be used |
| // when carrier's setting is turn off. |
| case TelephonyManager.CDMA_ROAMING_MODE_HOME: |
| case TelephonyManager.CDMA_ROAMING_MODE_AFFILIATED: |
| case TelephonyManager.CDMA_ROAMING_MODE_ANY: |
| logd("cdma_roaming_mode is going to changed to " |
| + config_cdma_roaming_mode); |
| setCdmaRoamingPreference(config_cdma_roaming_mode, |
| obtainMessage(EVENT_SET_ROAMING_PREFERENCE_DONE)); |
| break; |
| |
| // When carrier's setting is turn off, change the cdma_roaming_mode to the |
| // previous user's setting |
| case TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT: |
| if (current_cdma_roaming_mode != config_cdma_roaming_mode) { |
| logd("cdma_roaming_mode is going to changed to " |
| + current_cdma_roaming_mode); |
| setCdmaRoamingPreference(current_cdma_roaming_mode, |
| obtainMessage(EVENT_SET_ROAMING_PREFERENCE_DONE)); |
| } |
| |
| default: |
| loge("Invalid cdma_roaming_mode settings: " |
| + config_cdma_roaming_mode); |
| } |
| } else { |
| loge("didn't get the cdma_roaming_mode changes from the carrier config."); |
| } |
| |
| // Load the ERI based on carrier config. Carrier might have their specific ERI. |
| prepareEri(); |
| if (!isPhoneTypeGsm()) { |
| mSST.pollState(); |
| } |
| |
| break; |
| |
| case EVENT_SET_ROAMING_PREFERENCE_DONE: |
| logd("cdma_roaming_mode change is done"); |
| break; |
| |
| case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED: |
| logd("EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED"); |
| mCdmaSubscriptionSource = mCdmaSSM.getCdmaSubscriptionSource(); |
| break; |
| |
| case EVENT_REGISTERED_TO_NETWORK: |
| logd("Event EVENT_REGISTERED_TO_NETWORK Received"); |
| if (isPhoneTypeGsm()) { |
| syncClirSetting(); |
| } |
| break; |
| |
| case EVENT_SIM_RECORDS_LOADED: |
| updateCurrentCarrierInProvider(); |
| |
| // Check if this is a different SIM than the previous one. If so unset the |
| // voice mail number. |
| String imsi = getVmSimImsi(); |
| String imsiFromSIM = getSubscriberId(); |
| if ((!isPhoneTypeGsm() || imsi != null) && imsiFromSIM != null |
| && !imsiFromSIM.equals(imsi)) { |
| storeVoiceMailNumber(null); |
| setVmSimImsi(null); |
| } |
| |
| mSimRecordsLoadedRegistrants.notifyRegistrants(); |
| break; |
| |
| case EVENT_GET_BASEBAND_VERSION_DONE: |
| ar = (AsyncResult)msg.obj; |
| |
| if (ar.exception != null) { |
| break; |
| } |
| |
| if (DBG) logd("Baseband version: " + ar.result); |
| TelephonyManager.from(mContext).setBasebandVersionForPhone(getPhoneId(), |
| (String)ar.result); |
| break; |
| |
| case EVENT_GET_IMEI_DONE: |
| ar = (AsyncResult)msg.obj; |
| |
| if (ar.exception != null) { |
| break; |
| } |
| |
| mImei = (String)ar.result; |
| break; |
| |
| case EVENT_GET_IMEISV_DONE: |
| ar = (AsyncResult)msg.obj; |
| |
| if (ar.exception != null) { |
| break; |
| } |
| |
| mImeiSv = (String)ar.result; |
| break; |
| |
| case EVENT_USSD: |
| ar = (AsyncResult)msg.obj; |
| |
| String[] ussdResult = (String[]) ar.result; |
| |
| if (ussdResult.length > 1) { |
| try { |
| onIncomingUSSD(Integer.parseInt(ussdResult[0]), ussdResult[1]); |
| } catch (NumberFormatException e) { |
| Rlog.w(LOG_TAG, "error parsing USSD"); |
| } |
| } |
| break; |
| |
| case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: { |
| logd("Event EVENT_RADIO_OFF_OR_NOT_AVAILABLE Received"); |
| handleRadioOffOrNotAvailable(); |
| break; |
| } |
| |
| case EVENT_SSN: |
| logd("Event EVENT_SSN Received"); |
| if (isPhoneTypeGsm()) { |
| ar = (AsyncResult) msg.obj; |
| SuppServiceNotification not = (SuppServiceNotification) ar.result; |
| mSsnRegistrants.notifyRegistrants(ar); |
| } |
| break; |
| |
| case EVENT_SET_CALL_FORWARD_DONE: |
| ar = (AsyncResult)msg.obj; |
| IccRecords r = mIccRecords.get(); |
| Cfu cfu = (Cfu) ar.userObj; |
| if (ar.exception == null && r != null) { |
| setVoiceCallForwardingFlag(1, msg.arg1 == 1, cfu.mSetCfNumber); |
| } |
| if (cfu.mOnComplete != null) { |
| AsyncResult.forMessage(cfu.mOnComplete, ar.result, ar.exception); |
| cfu.mOnComplete.sendToTarget(); |
| } |
| break; |
| |
| case EVENT_SET_VM_NUMBER_DONE: |
| ar = (AsyncResult)msg.obj; |
| if ((isPhoneTypeGsm() && IccVmNotSupportedException.class.isInstance(ar.exception)) || |
| (!isPhoneTypeGsm() && IccException.class.isInstance(ar.exception))){ |
| storeVoiceMailNumber(mVmNumber); |
| ar.exception = null; |
| } |
| onComplete = (Message) ar.userObj; |
| if (onComplete != null) { |
| AsyncResult.forMessage(onComplete, ar.result, ar.exception); |
| onComplete.sendToTarget(); |
| } |
| break; |
| |
| |
| case EVENT_GET_CALL_FORWARD_DONE: |
| ar = (AsyncResult)msg.obj; |
| if (ar.exception == null) { |
| handleCfuQueryResult((CallForwardInfo[])ar.result); |
| } |
| onComplete = (Message) ar.userObj; |
| if (onComplete != null) { |
| AsyncResult.forMessage(onComplete, ar.result, ar.exception); |
| onComplete.sendToTarget(); |
| } |
| break; |
| |
| case EVENT_SET_NETWORK_AUTOMATIC: |
| // Automatic network selection from EF_CSP SIM record |
| ar = (AsyncResult) msg.obj; |
| if (mSST.mSS.getIsManualSelection()) { |
| setNetworkSelectionModeAutomatic((Message) ar.result); |
| logd("SET_NETWORK_SELECTION_AUTOMATIC: set to automatic"); |
| } else { |
| // prevent duplicate request which will push current PLMN to low priority |
| logd("SET_NETWORK_SELECTION_AUTOMATIC: already automatic, ignore"); |
| } |
| break; |
| |
| case EVENT_ICC_RECORD_EVENTS: |
| ar = (AsyncResult)msg.obj; |
| processIccRecordEvents((Integer)ar.result); |
| break; |
| |
| case EVENT_SET_CLIR_COMPLETE: |
| ar = (AsyncResult)msg.obj; |
| if (ar.exception == null) { |
| saveClirSetting(msg.arg1); |
| } |
| onComplete = (Message) ar.userObj; |
| if (onComplete != null) { |
| AsyncResult.forMessage(onComplete, ar.result, ar.exception); |
| onComplete.sendToTarget(); |
| } |
| break; |
| |
| case EVENT_SS: |
| ar = (AsyncResult)msg.obj; |
| logd("Event EVENT_SS received"); |
| if (isPhoneTypeGsm()) { |
| // SS data is already being handled through MMI codes. |
| // So, this result if processed as MMI response would help |
| // in re-using the existing functionality. |
| GsmMmiCode mmi = new GsmMmiCode(this, mUiccApplication.get()); |
| mmi.processSsData(ar); |
| } |
| break; |
| |
| case EVENT_GET_RADIO_CAPABILITY: |
| ar = (AsyncResult) msg.obj; |
| RadioCapability rc = (RadioCapability) ar.result; |
| if (ar.exception != null) { |
| Rlog.d(LOG_TAG, "get phone radio capability fail, no need to change " + |
| "mRadioCapability"); |
| } else { |
| radioCapabilityUpdated(rc); |
| } |
| Rlog.d(LOG_TAG, "EVENT_GET_RADIO_CAPABILITY: phone rc: " + rc); |
| break; |
| |
| default: |
| super.handleMessage(msg); |
| } |
| } |
| |
| public UiccCardApplication getUiccCardApplication() { |
| if (isPhoneTypeGsm()) { |
| return mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP); |
| } else { |
| return mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP2); |
| } |
| } |
| |
| @Override |
| protected void onUpdateIccAvailability() { |
| if (mUiccController == null ) { |
| return; |
| } |
| |
| UiccCardApplication newUiccApplication = null; |
| |
| // Update mIsimUiccRecords |
| if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) { |
| newUiccApplication = |
| mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_IMS); |
| IsimUiccRecords newIsimUiccRecords = null; |
| |
| if (newUiccApplication != null) { |
| newIsimUiccRecords = (IsimUiccRecords) newUiccApplication.getIccRecords(); |
| if (DBG) logd("New ISIM application found"); |
| } |
| mIsimUiccRecords = newIsimUiccRecords; |
| } |
| |
| // Update mSimRecords |
| if (mSimRecords != null) { |
| mSimRecords.unregisterForRecordsLoaded(this); |
| } |
| if (isPhoneTypeCdmaLte()) { |
| newUiccApplication = mUiccController.getUiccCardApplication(mPhoneId, |
| UiccController.APP_FAM_3GPP); |
| SIMRecords newSimRecords = null; |
| if (newUiccApplication != null) { |
| newSimRecords = (SIMRecords) newUiccApplication.getIccRecords(); |
| } |
| mSimRecords = newSimRecords; |
| if (mSimRecords != null) { |
| mSimRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); |
| } |
| } else { |
| mSimRecords = null; |
| } |
| |
| // Update mIccRecords, mUiccApplication, mIccPhoneBookIntManager |
| newUiccApplication = getUiccCardApplication(); |
| if (!isPhoneTypeGsm() && newUiccApplication == null) { |
| logd("can't find 3GPP2 application; trying APP_FAM_3GPP"); |
| newUiccApplication = mUiccController.getUiccCardApplication(mPhoneId, |
| UiccController.APP_FAM_3GPP); |
| } |
| |
| UiccCardApplication app = mUiccApplication.get(); |
| if (app != newUiccApplication) { |
| if (app != null) { |
| if (DBG) logd("Removing stale icc objects."); |
| if (mIccRecords.get() != null) { |
| unregisterForIccRecordEvents(); |
| mIccPhoneBookIntManager.updateIccRecords(null); |
| } |
| mIccRecords.set(null); |
| mUiccApplication.set(null); |
| } |
| if (newUiccApplication != null) { |
| if (DBG) { |
| logd("New Uicc application found. type = " + newUiccApplication.getType()); |
| } |
| mUiccApplication.set(newUiccApplication); |
| mIccRecords.set(newUiccApplication.getIccRecords()); |
| registerForIccRecordEvents(); |
| mIccPhoneBookIntManager.updateIccRecords(mIccRecords.get()); |
| } |
| } |
| } |
| |
| private void processIccRecordEvents(int eventCode) { |
| switch (eventCode) { |
| case IccRecords.EVENT_CFI: |
| logi("processIccRecordEvents: EVENT_CFI"); |
| notifyCallForwardingIndicator(); |
| break; |
| } |
| } |
| |
| /** |
| * Sets the "current" field in the telephony provider according to the SIM's operator |
| * |
| * @return true for success; false otherwise. |
| */ |
| @Override |
| public boolean updateCurrentCarrierInProvider() { |
| if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) { |
| long currentDds = SubscriptionManager.getDefaultDataSubscriptionId(); |
| String operatorNumeric = getOperatorNumeric(); |
| |
| logd("updateCurrentCarrierInProvider: mSubId = " + getSubId() |
| + " currentDds = " + currentDds + " operatorNumeric = " + operatorNumeric); |
| |
| if (!TextUtils.isEmpty(operatorNumeric) && (getSubId() == currentDds)) { |
| try { |
| Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"); |
| ContentValues map = new ContentValues(); |
| map.put(Telephony.Carriers.NUMERIC, operatorNumeric); |
| mContext.getContentResolver().insert(uri, map); |
| return true; |
| } catch (SQLException e) { |
| Rlog.e(LOG_TAG, "Can't store current operator", e); |
| } |
| } |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| //CDMA |
| /** |
| * Sets the "current" field in the telephony provider according to the |
| * build-time operator numeric property |
| * |
| * @return true for success; false otherwise. |
| */ |
| private boolean updateCurrentCarrierInProvider(String operatorNumeric) { |
| if (isPhoneTypeCdma() |
| || (isPhoneTypeCdmaLte() && mUiccController.getUiccCardApplication(mPhoneId, |
| UiccController.APP_FAM_3GPP) == null)) { |
| logd("CDMAPhone: updateCurrentCarrierInProvider called"); |
| if (!TextUtils.isEmpty(operatorNumeric)) { |
| try { |
| Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"); |
| ContentValues map = new ContentValues(); |
| map.put(Telephony.Carriers.NUMERIC, operatorNumeric); |
| logd("updateCurrentCarrierInProvider from system: numeric=" + operatorNumeric); |
| getContext().getContentResolver().insert(uri, map); |
| |
| // Updates MCC MNC device configuration information |
| logd("update mccmnc=" + operatorNumeric); |
| MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false); |
| |
| return true; |
| } catch (SQLException e) { |
| Rlog.e(LOG_TAG, "Can't store current operator", e); |
| } |
| } |
| return false; |
| } else { // isPhoneTypeCdmaLte() |
| if (DBG) logd("updateCurrentCarrierInProvider not updated X retVal=" + true); |
| return true; |
| } |
| } |
| |
| private void handleCfuQueryResult(CallForwardInfo[] infos) { |
| IccRecords r = mIccRecords.get(); |
| if (r != null) { |
| if (infos == null || infos.length == 0) { |
| // Assume the default is not active |
| // Set unconditional CFF in SIM to false |
| setVoiceCallForwardingFlag(1, false, null); |
| } else { |
| for (int i = 0, s = infos.length; i < s; i++) { |
| if ((infos[i].serviceClass & SERVICE_CLASS_VOICE) != 0) { |
| setVoiceCallForwardingFlag(1, (infos[i].status == 1), |
| infos[i].number); |
| // should only have the one |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Retrieves the IccPhoneBookInterfaceManager of the GsmCdmaPhone |
| */ |
| @Override |
| public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){ |
| return mIccPhoneBookIntManager; |
| } |
| |
| //CDMA |
| public void registerForEriFileLoaded(Handler h, int what, Object obj) { |
| Registrant r = new Registrant (h, what, obj); |
| mEriFileLoadedRegistrants.add(r); |
| } |
| |
| //CDMA |
| public void unregisterForEriFileLoaded(Handler h) { |
| mEriFileLoadedRegistrants.remove(h); |
| } |
| |
| //CDMA |
| public void prepareEri() { |
| if (mEriManager == null) { |
| Rlog.e(LOG_TAG, "PrepareEri: Trying to access stale objects"); |
| return; |
| } |
| mEriManager.loadEriFile(); |
| if(mEriManager.isEriFileLoaded()) { |
| // when the ERI file is loaded |
| logd("ERI read, notify registrants"); |
| mEriFileLoadedRegistrants.notifyRegistrants(); |
| } |
| } |
| |
| //CDMA |
| public boolean isEriFileLoaded() { |
| return mEriManager.isEriFileLoaded(); |
| } |
| |
| |
| /** |
| * Activate or deactivate cell broadcast SMS. |
| * |
| * @param activate 0 = activate, 1 = deactivate |
| * @param response Callback message is empty on completion |
| */ |
| @Override |
| public void activateCellBroadcastSms(int activate, Message response) { |
| loge("[GsmCdmaPhone] activateCellBroadcastSms() is obsolete; use SmsManager"); |
| response.sendToTarget(); |
| } |
| |
| /** |
| * Query the current configuration of cdma cell broadcast SMS. |
| * |
| * @param response Callback message is empty on completion |
| */ |
| @Override |
| public void getCellBroadcastSmsConfig(Message response) { |
| loge("[GsmCdmaPhone] getCellBroadcastSmsConfig() is obsolete; use SmsManager"); |
| response.sendToTarget(); |
| } |
| |
| /** |
| * Configure cdma cell broadcast SMS. |
| * |
| * @param response Callback message is empty on completion |
| */ |
| @Override |
| public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) { |
| loge("[GsmCdmaPhone] setCellBroadcastSmsConfig() is obsolete; use SmsManager"); |
| response.sendToTarget(); |
| } |
| |
| /** |
| * Returns true if OTA Service Provisioning needs to be performed. |
| */ |
| @Override |
| public boolean needsOtaServiceProvisioning() { |
| if (isPhoneTypeGsm()) { |
| return false; |
| } else { |
| return mSST.getOtasp() != TelephonyManager.OTASP_NOT_NEEDED; |
| } |
| } |
| |
| @Override |
| public boolean isCspPlmnEnabled() { |
| IccRecords r = mIccRecords.get(); |
| return (r != null) ? r.isCspPlmnEnabled() : false; |
| } |
| |
| /** |
| * Whether manual select is now allowed and we should set |
| * to auto network select mode. |
| */ |
| public boolean shouldForceAutoNetworkSelect() { |
| |
| int nwMode = Phone.PREFERRED_NT_MODE; |
| int subId = getSubId(); |
| |
| // If it's invalid subId, we shouldn't force to auto network select mode. |
| if (!SubscriptionManager.isValidSubscriptionId(subId)) { |
| return false; |
| } |
| |
| nwMode = android.provider.Settings.Global.getInt(mContext.getContentResolver(), |
| android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId, nwMode); |
| |
| logd("shouldForceAutoNetworkSelect in mode = " + nwMode); |
| /* |
| * For multimode targets in global mode manual network |
| * selection is disallowed. So we should force auto select mode. |
| */ |
| if (isManualSelProhibitedInGlobalMode() |
| && ((nwMode == Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA) |
| || (nwMode == Phone.NT_MODE_GLOBAL)) ){ |
| logd("Should force auto network select mode = " + nwMode); |
| return true; |
| } else { |
| logd("Should not force auto network select mode = " + nwMode); |
| } |
| |
| /* |
| * Single mode phone with - GSM network modes/global mode |
| * LTE only for 3GPP |
| * LTE centric + 3GPP Legacy |
| * Note: the actual enabling/disabling manual selection for these |
| * cases will be controlled by csp |
| */ |
| return false; |
| } |
| |
| private boolean isManualSelProhibitedInGlobalMode() { |
| boolean isProhibited = false; |
| final String configString = getContext().getResources().getString(com.android.internal. |
| R.string.prohibit_manual_network_selection_in_gobal_mode); |
| |
| if (!TextUtils.isEmpty(configString)) { |
| String[] configArray = configString.split(";"); |
| |
| if (configArray != null && |
| ((configArray.length == 1 && configArray[0].equalsIgnoreCase("true")) || |
| (configArray.length == 2 && !TextUtils.isEmpty(configArray[1]) && |
| configArray[0].equalsIgnoreCase("true") && |
| isMatchGid(configArray[1])))) { |
| isProhibited = true; |
| } |
| } |
| logd("isManualNetSelAllowedInGlobal in current carrier is " + isProhibited); |
| return isProhibited; |
| } |
| |
| private void registerForIccRecordEvents() { |
| IccRecords r = mIccRecords.get(); |
| if (r == null) { |
| return; |
| } |
| if (isPhoneTypeGsm()) { |
| r.registerForNetworkSelectionModeAutomatic( |
| this, EVENT_SET_NETWORK_AUTOMATIC, null); |
| r.registerForRecordsEvents(this, EVENT_ICC_RECORD_EVENTS, null); |
| r.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); |
| } else { |
| r.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null); |
| if (isPhoneTypeCdmaLte()) { |
| // notify simRecordsLoaded registrants for cdmaLte phone |
| r.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); |
| } |
| } |
| } |
| |
| private void unregisterForIccRecordEvents() { |
| IccRecords r = mIccRecords.get(); |
| if (r == null) { |
| return; |
| } |
| r.unregisterForNetworkSelectionModeAutomatic(this); |
| r.unregisterForRecordsEvents(this); |
| r.unregisterForRecordsLoaded(this); |
| } |
| |
| @Override |
| public void exitEmergencyCallbackMode() { |
| if (DBG) { |
| Rlog.d(LOG_TAG, "exitEmergencyCallbackMode: mImsPhone=" + mImsPhone |
| + " isPhoneTypeGsm=" + isPhoneTypeGsm()); |
| } |
| if (isPhoneTypeGsm()) { |
| if (mImsPhone != null) { |
| mImsPhone.exitEmergencyCallbackMode(); |
| } |
| } else { |
| if (mWakeLock.isHeld()) { |
| mWakeLock.release(); |
| } |
| // Send a message which will invoke handleExitEmergencyCallbackMode |
| mCi.exitEmergencyCallbackMode(obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE)); |
| } |
| } |
| |
| //CDMA |
| private void handleEnterEmergencyCallbackMode(Message msg) { |
| if (DBG) { |
| Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode, isInEcm()=" |
| + isInEcm()); |
| } |
| // if phone is not in Ecm mode, and it's changed to Ecm mode |
| if (!isInEcm()) { |
| setIsInEcm(true); |
| |
| // notify change |
| sendEmergencyCallbackModeChange(); |
| |
| // Post this runnable so we will automatically exit |
| // if no one invokes exitEmergencyCallbackMode() directly. |
| long delayInMillis = SystemProperties.getLong( |
| TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE); |
| postDelayed(mExitEcmRunnable, delayInMillis); |
| // We don't want to go to sleep while in Ecm |
| mWakeLock.acquire(); |
| } |
| } |
| |
| //CDMA |
| private void handleExitEmergencyCallbackMode(Message msg) { |
| AsyncResult ar = (AsyncResult)msg.obj; |
| if (DBG) { |
| Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode,ar.exception , isInEcm=" |
| + ar.exception + isInEcm()); |
| } |
| // Remove pending exit Ecm runnable, if any |
| removeCallbacks(mExitEcmRunnable); |
| |
| if (mEcmExitRespRegistrant != null) { |
| mEcmExitRespRegistrant.notifyRegistrant(ar); |
| } |
| // if exiting ecm success |
| if (ar.exception == null) { |
| if (isInEcm()) { |
| setIsInEcm(false); |
| } |
| |
| // release wakeLock |
| if (mWakeLock.isHeld()) { |
| mWakeLock.release(); |
| } |
| |
| // send an Intent |
| sendEmergencyCallbackModeChange(); |
| // Re-initiate data connection |
| mDcTracker.setInternalDataEnabled(true); |
| notifyEmergencyCallRegistrants(false); |
| } |
| } |
| |
| //CDMA |
| public void notifyEmergencyCallRegistrants(boolean started) { |
| mEmergencyCallToggledRegistrants.notifyResult(started ? 1 : 0); |
| } |
| |
| //CDMA |
| /** |
| * Handle to cancel or restart Ecm timer in emergency call back mode |
| * if action is CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled; |
| * otherwise, restart Ecm timer and notify apps the timer is restarted. |
| */ |
| public void handleTimerInEmergencyCallbackMode(int action) { |
| switch(action) { |
| case CANCEL_ECM_TIMER: |
| removeCallbacks(mExitEcmRunnable); |
| mEcmTimerResetRegistrants.notifyResult(Boolean.TRUE); |
| break; |
| case RESTART_ECM_TIMER: |
| long delayInMillis = SystemProperties.getLong( |
| TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE); |
| postDelayed(mExitEcmRunnable, delayInMillis); |
| mEcmTimerResetRegistrants.notifyResult(Boolean.FALSE); |
| break; |
| default: |
| Rlog.e(LOG_TAG, "handleTimerInEmergencyCallbackMode, unsupported action " + action); |
| } |
| } |
| |
| //CDMA |
| private static final String IS683A_FEATURE_CODE = "*228"; |
| private static final int IS683A_FEATURE_CODE_NUM_DIGITS = 4; |
| private static final int IS683A_SYS_SEL_CODE_NUM_DIGITS = 2; |
| private static final int IS683A_SYS_SEL_CODE_OFFSET = 4; |
| |
| private static final int IS683_CONST_800MHZ_A_BAND = 0; |
| private static final int IS683_CONST_800MHZ_B_BAND = 1; |
| private static final int IS683_CONST_1900MHZ_A_BLOCK = 2; |
| private static final int IS683_CONST_1900MHZ_B_BLOCK = 3; |
| private static final int IS683_CONST_1900MHZ_C_BLOCK = 4; |
| private static final int IS683_CONST_1900MHZ_D_BLOCK = 5; |
| private static final int IS683_CONST_1900MHZ_E_BLOCK = 6; |
| private static final int IS683_CONST_1900MHZ_F_BLOCK = 7; |
| private static final int INVALID_SYSTEM_SELECTION_CODE = -1; |
| |
| // Define the pattern/format for carrier specified OTASP number schema. |
| // It separates by comma and/or whitespace. |
| private static Pattern pOtaSpNumSchema = Pattern.compile("[,\\s]+"); |
| |
| //CDMA |
| private static boolean isIs683OtaSpDialStr(String dialStr) { |
| int sysSelCodeInt; |
| boolean isOtaspDialString = false; |
| int dialStrLen = dialStr.length(); |
| |
| if (dialStrLen == IS683A_FEATURE_CODE_NUM_DIGITS) { |
| if (dialStr.equals(IS683A_FEATURE_CODE)) { |
| isOtaspDialString = true; |
| } |
| } else { |
| sysSelCodeInt = extractSelCodeFromOtaSpNum(dialStr); |
| switch (sysSelCodeInt) { |
| case IS683_CONST_800MHZ_A_BAND: |
| case IS683_CONST_800MHZ_B_BAND: |
| case IS683_CONST_1900MHZ_A_BLOCK: |
| case IS683_CONST_1900MHZ_B_BLOCK: |
| case IS683_CONST_1900MHZ_C_BLOCK: |
| case IS683_CONST_1900MHZ_D_BLOCK: |
| case IS683_CONST_1900MHZ_E_BLOCK: |
| case IS683_CONST_1900MHZ_F_BLOCK: |
| isOtaspDialString = true; |
| break; |
| default: |
| break; |
| } |
| } |
| return isOtaspDialString; |
| } |
| |
| //CDMA |
| /** |
| * This function extracts the system selection code from the dial string. |
| */ |
| private static int extractSelCodeFromOtaSpNum(String dialStr) { |
| int dialStrLen = dialStr.length(); |
| int sysSelCodeInt = INVALID_SYSTEM_SELECTION_CODE; |
| |
| if ((dialStr.regionMatches(0, IS683A_FEATURE_CODE, |
| 0, IS683A_FEATURE_CODE_NUM_DIGITS)) && |
| (dialStrLen >= (IS683A_FEATURE_CODE_NUM_DIGITS + |
| IS683A_SYS_SEL_CODE_NUM_DIGITS))) { |
| // Since we checked the condition above, the system selection code |
| // extracted from dialStr will not cause any exception |
| sysSelCodeInt = Integer.parseInt ( |
| dialStr.substring (IS683A_FEATURE_CODE_NUM_DIGITS, |
| IS683A_FEATURE_CODE_NUM_DIGITS + IS683A_SYS_SEL_CODE_NUM_DIGITS)); |
| } |
| if (DBG) Rlog.d(LOG_TAG, "extractSelCodeFromOtaSpNum " + sysSelCodeInt); |
| return sysSelCodeInt; |
| } |
| |
| //CDMA |
| /** |
| * This function checks if the system selection code extracted from |
| * the dial string "sysSelCodeInt' is the system selection code specified |
| * in the carrier ota sp number schema "sch". |
| */ |
| private static boolean checkOtaSpNumBasedOnSysSelCode(int sysSelCodeInt, String sch[]) { |
| boolean isOtaSpNum = false; |
| try { |
| // Get how many number of system selection code ranges |
| int selRc = Integer.parseInt(sch[1]); |
| for (int i = 0; i < selRc; i++) { |
| if (!TextUtils.isEmpty(sch[i+2]) && !TextUtils.isEmpty(sch[i+3])) { |
| int selMin = Integer.parseInt(sch[i+2]); |
| int selMax = Integer.parseInt(sch[i+3]); |
| // Check if the selection code extracted from the dial string falls |
| // within any of the range pairs specified in the schema. |
| if ((sysSelCodeInt >= selMin) && (sysSelCodeInt <= selMax)) { |
| isOtaSpNum = true; |
| break; |
| } |
| } |
| } |
| } catch (NumberFormatException ex) { |
| // If the carrier ota sp number schema is not correct, we still allow dial |
| // and only log the error: |
| Rlog.e(LOG_TAG, "checkOtaSpNumBasedOnSysSelCode, error", ex); |
| } |
| return isOtaSpNum; |
| } |
| |
| //CDMA |
| /** |
| * The following function checks if a dial string is a carrier specified |
| * OTASP number or not by checking against the OTASP number schema stored |
| * in PROPERTY_OTASP_NUM_SCHEMA. |
| * |
| * Currently, there are 2 schemas for carriers to specify the OTASP number: |
| * 1) Use system selection code: |
| * The schema is: |
| * SELC,the # of code pairs,min1,max1,min2,max2,... |
| * e.g "SELC,3,10,20,30,40,60,70" indicates that there are 3 pairs of |
| * selection codes, and they are {10,20}, {30,40} and {60,70} respectively. |
| * |
| * 2) Use feature code: |
| * The schema is: |
| * "FC,length of feature code,feature code". |
| * e.g "FC,2,*2" indicates that the length of the feature code is 2, |
| * and the code itself is "*2". |
| */ |
| private boolean isCarrierOtaSpNum(String dialStr) { |
| boolean isOtaSpNum = false; |
| int sysSelCodeInt = extractSelCodeFromOtaSpNum(dialStr); |
| if (sysSelCodeInt == INVALID_SYSTEM_SELECTION_CODE) { |
| return isOtaSpNum; |
| } |
| // mCarrierOtaSpNumSchema is retrieved from PROPERTY_OTASP_NUM_SCHEMA: |
| if (!TextUtils.isEmpty(mCarrierOtaSpNumSchema)) { |
| Matcher m = pOtaSpNumSchema.matcher(mCarrierOtaSpNumSchema); |
| if (DBG) { |
| Rlog.d(LOG_TAG, "isCarrierOtaSpNum,schema" + mCarrierOtaSpNumSchema); |
| } |
| |
| if (m.find()) { |
| String sch[] = pOtaSpNumSchema.split(mCarrierOtaSpNumSchema); |
| // If carrier uses system selection code mechanism |
| if (!TextUtils.isEmpty(sch[0]) && sch[0].equals("SELC")) { |
| if (sysSelCodeInt!=INVALID_SYSTEM_SELECTION_CODE) { |
| isOtaSpNum=checkOtaSpNumBasedOnSysSelCode(sysSelCodeInt,sch); |
| } else { |
| if (DBG) { |
| Rlog.d(LOG_TAG, "isCarrierOtaSpNum,sysSelCodeInt is invalid"); |
| } |
| } |
| } else if (!TextUtils.isEmpty(sch[0]) && sch[0].equals("FC")) { |
| int fcLen = Integer.parseInt(sch[1]); |
| String fc = sch[2]; |
| if (dialStr.regionMatches(0,fc,0,fcLen)) { |
| isOtaSpNum = true; |
| } else { |
| if (DBG) Rlog.d(LOG_TAG, "isCarrierOtaSpNum,not otasp number"); |
| } |
| } else { |
| if (DBG) { |
| Rlog.d(LOG_TAG, "isCarrierOtaSpNum,ota schema not supported" + sch[0]); |
| } |
| } |
| } else { |
| if (DBG) { |
| Rlog.d(LOG_TAG, "isCarrierOtaSpNum,ota schema pattern not right" + |
| mCarrierOtaSpNumSchema); |
| } |
| } |
| } else { |
| if (DBG) Rlog.d(LOG_TAG, "isCarrierOtaSpNum,ota schema pattern empty"); |
| } |
| return isOtaSpNum; |
| } |
| |
| /** |
| * isOTASPNumber: checks a given number against the IS-683A OTASP dial string and carrier |
| * OTASP dial string. |
| * |
| * @param dialStr the number to look up. |
| * @return true if the number is in IS-683A OTASP dial string or carrier OTASP dial string |
| */ |
| @Override |
| public boolean isOtaSpNumber(String dialStr) { |
| if (isPhoneTypeGsm()) { |
| return super.isOtaSpNumber(dialStr); |
| } else { |
| boolean isOtaSpNum = false; |
| String dialableStr = PhoneNumberUtils.extractNetworkPortionAlt(dialStr); |
| if (dialableStr != null) { |
| isOtaSpNum = isIs683OtaSpDialStr(dialableStr); |
| if (isOtaSpNum == false) { |
| isOtaSpNum = isCarrierOtaSpNum(dialableStr); |
| } |
| } |
| if (DBG) Rlog.d(LOG_TAG, "isOtaSpNumber " + isOtaSpNum); |
| return isOtaSpNum; |
| } |
| } |
| |
| @Override |
| public int getCdmaEriIconIndex() { |
| if (isPhoneTypeGsm()) { |
| return super.getCdmaEriIconIndex(); |
| } else { |
| return getServiceState().getCdmaEriIconIndex(); |
| } |
| } |
| |
| /** |
| * Returns the CDMA ERI icon mode, |
| * 0 - ON |
| * 1 - FLASHING |
| */ |
| @Override |
| public int getCdmaEriIconMode() { |
| if (isPhoneTypeGsm()) { |
| return super.getCdmaEriIconMode(); |
| } else { |
| return getServiceState().getCdmaEriIconMode(); |
| } |
| } |
| |
| /** |
| * Returns the CDMA ERI text, |
| */ |
| @Override |
| public String getCdmaEriText() { |
| if (isPhoneTypeGsm()) { |
| return super.getCdmaEriText(); |
| } else { |
| int roamInd = getServiceState().getCdmaRoamingIndicator(); |
| int defRoamInd = getServiceState().getCdmaDefaultRoamingIndicator(); |
| return mEriManager.getCdmaEriText(roamInd, defRoamInd); |
| } |
| } |
| |
| private void phoneObjectUpdater(int newVoiceRadioTech) { |
| logd("phoneObjectUpdater: newVoiceRadioTech=" + newVoiceRadioTech); |
| |
| // Check for a voice over lte replacement |
| if (ServiceState.isLte(newVoiceRadioTech) |
| || (newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)) { |
| CarrierConfigManager configMgr = (CarrierConfigManager) |
| getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle b = configMgr.getConfigForSubId(getSubId()); |
| if (b != null) { |
| int volteReplacementRat = |
| b.getInt(CarrierConfigManager.KEY_VOLTE_REPLACEMENT_RAT_INT); |
| logd("phoneObjectUpdater: volteReplacementRat=" + volteReplacementRat); |
| if (volteReplacementRat != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) { |
| newVoiceRadioTech = volteReplacementRat; |
| } |
| } else { |
| loge("phoneObjectUpdater: didn't get volteReplacementRat from carrier config"); |
| } |
| } |
| |
| if(mRilVersion == 6 && getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE) { |
| /* |
| * On v6 RIL, when LTE_ON_CDMA is TRUE, always create CDMALTEPhone |
| * irrespective of the voice radio tech reported. |
| */ |
| if (getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { |
| logd("phoneObjectUpdater: LTE ON CDMA property is set. Use CDMA Phone" + |
| " newVoiceRadioTech=" + newVoiceRadioTech + |
| " mActivePhone=" + getPhoneName()); |
| return; |
| } else { |
| logd("phoneObjectUpdater: LTE ON CDMA property is set. Switch to CDMALTEPhone" + |
| " newVoiceRadioTech=" + newVoiceRadioTech + |
| " mActivePhone=" + getPhoneName()); |
| newVoiceRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT; |
| } |
| } else { |
| |
| // If the device is shutting down, then there is no need to switch to the new phone |
| // which might send unnecessary attach request to the modem. |
| if (isShuttingDown()) { |
| logd("Device is shutting down. No need to switch phone now."); |
| return; |
| } |
| |
| boolean matchCdma = ServiceState.isCdma(newVoiceRadioTech); |
| boolean matchGsm = ServiceState.isGsm(newVoiceRadioTech); |
| if ((matchCdma && getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) || |
| (matchGsm && getPhoneType() == PhoneConstants.PHONE_TYPE_GSM)) { |
| // Nothing changed. Keep phone as it is. |
| logd("phoneObjectUpdater: No change ignore," + |
| " newVoiceRadioTech=" + newVoiceRadioTech + |
| " mActivePhone=" + getPhoneName()); |
| return; |
| } |
| if (!matchCdma && !matchGsm) { |
| loge("phoneObjectUpdater: newVoiceRadioTech=" + newVoiceRadioTech + |
| " doesn't match either CDMA or GSM - error! No phone change"); |
| return; |
| } |
| } |
| |
| if (newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) { |
| // We need some voice phone object to be active always, so never |
| // delete the phone without anything to replace it with! |
| logd("phoneObjectUpdater: Unknown rat ignore, " |
| + " newVoiceRadioTech=Unknown. mActivePhone=" + getPhoneName()); |
| return; |
| } |
| |
| boolean oldPowerState = false; // old power state to off |
| if (mResetModemOnRadioTechnologyChange) { |
| if (mCi.getRadioState().isOn()) { |
| oldPowerState = true; |
| logd("phoneObjectUpdater: Setting Radio Power to Off"); |
| mCi.setRadioPower(false, null); |
| } |
| } |
| |
| switchVoiceRadioTech(newVoiceRadioTech); |
| |
| if (mResetModemOnRadioTechnologyChange && oldPowerState) { // restore power state |
| logd("phoneObjectUpdater: Resetting Radio"); |
| mCi.setRadioPower(oldPowerState, null); |
| } |
| |
| // update voice radio tech in icc card proxy |
| mIccCardProxy.setVoiceRadioTech(newVoiceRadioTech); |
| |
| // Send an Intent to the PhoneApp that we had a radio technology change |
| Intent intent = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); |
| intent.putExtra(PhoneConstants.PHONE_NAME_KEY, getPhoneName()); |
| SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhoneId); |
| ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL); |
| } |
| |
| private void switchVoiceRadioTech(int newVoiceRadioTech) { |
| |
| String outgoingPhoneName = getPhoneName(); |
| |
| logd("Switching Voice Phone : " + outgoingPhoneName + " >>> " |
| + (ServiceState.isGsm(newVoiceRadioTech) ? "GSM" : "CDMA")); |
| |
| if (ServiceState.isCdma(newVoiceRadioTech)) { |
| switchPhoneType(PhoneConstants.PHONE_TYPE_CDMA_LTE); |
| } else if (ServiceState.isGsm(newVoiceRadioTech)) { |
| switchPhoneType(PhoneConstants.PHONE_TYPE_GSM); |
| } else { |
| loge("deleteAndCreatePhone: newVoiceRadioTech=" + newVoiceRadioTech + |
| " is not CDMA or GSM (error) - aborting!"); |
| return; |
| } |
| } |
| |
| @Override |
| public IccSmsInterfaceManager getIccSmsInterfaceManager(){ |
| return mIccSmsInterfaceManager; |
| } |
| |
| @Override |
| public void updatePhoneObject(int voiceRadioTech) { |
| logd("updatePhoneObject: radioTechnology=" + voiceRadioTech); |
| sendMessage(obtainMessage(EVENT_UPDATE_PHONE_OBJECT, voiceRadioTech, 0, null)); |
| } |
| |
| @Override |
| public void setImsRegistrationState(boolean registered) { |
| mSST.setImsRegistrationState(registered); |
| } |
| |
| @Override |
| public boolean getIccRecordsLoaded() { |
| return mIccCardProxy.getIccRecordsLoaded(); |
| } |
| |
| @Override |
| public IccCard getIccCard() { |
| return mIccCardProxy; |
| } |
| |
| @Override |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| pw.println("GsmCdmaPhone extends:"); |
| super.dump(fd, pw, args); |
| pw.println(" mPrecisePhoneType=" + mPrecisePhoneType); |
| pw.println(" mCT=" + mCT); |
| pw.println(" mSST=" + mSST); |
| pw.println(" mPendingMMIs=" + mPendingMMIs); |
| pw.println(" mIccPhoneBookIntManager=" + mIccPhoneBookIntManager); |
| if (VDBG) pw.println(" mImei=" + mImei); |
| if (VDBG) pw.println(" mImeiSv=" + mImeiSv); |
| if (VDBG) pw.println(" mVmNumber=" + mVmNumber); |
| pw.println(" mCdmaSSM=" + mCdmaSSM); |
| pw.println(" mCdmaSubscriptionSource=" + mCdmaSubscriptionSource); |
| pw.println(" mEriManager=" + mEriManager); |
| pw.println(" mWakeLock=" + mWakeLock); |
| pw.println(" isInEcm()=" + isInEcm()); |
| if (VDBG) pw.println(" mEsn=" + mEsn); |
| if (VDBG) pw.println(" mMeid=" + mMeid); |
| pw.println(" mCarrierOtaSpNumSchema=" + mCarrierOtaSpNumSchema); |
| if (!isPhoneTypeGsm()) { |
| pw.println(" getCdmaEriIconIndex()=" + getCdmaEriIconIndex()); |
| pw.println(" getCdmaEriIconMode()=" + getCdmaEriIconMode()); |
| pw.println(" getCdmaEriText()=" + getCdmaEriText()); |
| pw.println(" isMinInfoReady()=" + isMinInfoReady()); |
| } |
| pw.println(" isCspPlmnEnabled()=" + isCspPlmnEnabled()); |
| pw.flush(); |
| pw.println("++++++++++++++++++++++++++++++++"); |
| |
| try { |
| mIccCardProxy.dump(fd, pw, args); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| pw.flush(); |
| pw.println("++++++++++++++++++++++++++++++++"); |
| pw.println("DeviceStateMonitor:"); |
| mDeviceStateMonitor.dump(fd, pw, args); |
| pw.println("++++++++++++++++++++++++++++++++"); |
| } |
| |
| @Override |
| public boolean setOperatorBrandOverride(String brand) { |
| if (mUiccController == null) { |
| return false; |
| } |
| |
| UiccCard card = mUiccController.getUiccCard(getPhoneId()); |
| if (card == null) { |
| return false; |
| } |
| |
| boolean status = card.setOperatorBrandOverride(brand); |
| |
| // Refresh. |
| if (status) { |
| IccRecords iccRecords = mIccRecords.get(); |
| if (iccRecords != null) { |
| TelephonyManager.from(mContext).setSimOperatorNameForPhone( |
| getPhoneId(), iccRecords.getServiceProviderName()); |
| } |
| if (mSST != null) { |
| mSST.pollState(); |
| } |
| } |
| return status; |
| } |
| |
| /** |
| * @return operator numeric. |
| */ |
| private String getOperatorNumeric() { |
| String operatorNumeric = null; |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| if (r != null) { |
| operatorNumeric = r.getOperatorNumeric(); |
| } |
| } else { //isPhoneTypeCdmaLte() |
| IccRecords curIccRecords = null; |
| if (mCdmaSubscriptionSource == CDMA_SUBSCRIPTION_NV) { |
| operatorNumeric = SystemProperties.get("ro.cdma.home.operator.numeric"); |
| } else if (mCdmaSubscriptionSource == CDMA_SUBSCRIPTION_RUIM_SIM) { |
| curIccRecords = mSimRecords; |
| if (curIccRecords != null) { |
| operatorNumeric = curIccRecords.getOperatorNumeric(); |
| } else { |
| curIccRecords = mIccRecords.get(); |
| if (curIccRecords != null && (curIccRecords instanceof RuimRecords)) { |
| RuimRecords csim = (RuimRecords) curIccRecords; |
| operatorNumeric = csim.getRUIMOperatorNumeric(); |
| } |
| } |
| } |
| if (operatorNumeric == null) { |
| loge("getOperatorNumeric: Cannot retrieve operatorNumeric:" |
| + " mCdmaSubscriptionSource = " + mCdmaSubscriptionSource + |
| " mIccRecords = " + ((curIccRecords != null) ? |
| curIccRecords.getRecordsLoaded() : null)); |
| } |
| |
| logd("getOperatorNumeric: mCdmaSubscriptionSource = " + mCdmaSubscriptionSource |
| + " operatorNumeric = " + operatorNumeric); |
| |
| } |
| return operatorNumeric; |
| } |
| |
| /** |
| * @return The country ISO for the subscription associated with this phone. |
| */ |
| public String getCountryIso() { |
| int subId = getSubId(); |
| SubscriptionInfo subInfo = SubscriptionManager.from(getContext()) |
| .getActiveSubscriptionInfo(subId); |
| if (subInfo == null) { |
| return null; |
| } |
| return subInfo.getCountryIso().toUpperCase(); |
| } |
| |
| public void notifyEcbmTimerReset(Boolean flag) { |
| mEcmTimerResetRegistrants.notifyResult(flag); |
| } |
| |
| /** |
| * Registration point for Ecm timer reset |
| * |
| * @param h handler to notify |
| * @param what User-defined message code |
| * @param obj placed in Message.obj |
| */ |
| @Override |
| public void registerForEcmTimerReset(Handler h, int what, Object obj) { |
| mEcmTimerResetRegistrants.addUnique(h, what, obj); |
| } |
| |
| @Override |
| public void unregisterForEcmTimerReset(Handler h) { |
| mEcmTimerResetRegistrants.remove(h); |
| } |
| |
| /** |
| * Sets the SIM voice message waiting indicator records. |
| * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported |
| * @param countWaiting The number of messages waiting, if known. Use |
| * -1 to indicate that an unknown number of |
| * messages are waiting |
| */ |
| @Override |
| public void setVoiceMessageWaiting(int line, int countWaiting) { |
| if (isPhoneTypeGsm()) { |
| IccRecords r = mIccRecords.get(); |
| if (r != null) { |
| r.setVoiceMessageWaiting(line, countWaiting); |
| } else { |
| logd("SIM Records not found, MWI not updated"); |
| } |
| } else { |
| setVoiceMessageCount(countWaiting); |
| } |
| } |
| |
| private void logd(String s) { |
| Rlog.d(LOG_TAG, "[GsmCdmaPhone] " + s); |
| } |
| |
| private void logi(String s) { |
| Rlog.i(LOG_TAG, "[GsmCdmaPhone] " + s); |
| } |
| |
| private void loge(String s) { |
| Rlog.e(LOG_TAG, "[GsmCdmaPhone] " + s); |
| } |
| |
| @Override |
| public boolean isUtEnabled() { |
| Phone imsPhone = mImsPhone; |
| if (imsPhone != null) { |
| return imsPhone.isUtEnabled(); |
| } else { |
| logd("isUtEnabled: called for GsmCdma"); |
| return false; |
| } |
| } |
| |
| public String getDtmfToneDelayKey() { |
| return isPhoneTypeGsm() ? |
| CarrierConfigManager.KEY_GSM_DTMF_TONE_DELAY_INT : |
| CarrierConfigManager.KEY_CDMA_DTMF_TONE_DELAY_INT; |
| } |
| |
| @VisibleForTesting |
| public PowerManager.WakeLock getWakeLock() { |
| return mWakeLock; |
| } |
| |
| } |