/* * Copyright (C) 2006 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.cdma; import android.app.ActivityManagerNative; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.SQLException; import android.net.Uri; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.Registrant; import android.os.RegistrantList; import android.os.SystemProperties; import android.os.UserHandle; import android.preference.PreferenceManager; import android.provider.Settings; import android.provider.Telephony; import android.telephony.CellLocation; import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; import android.telephony.cdma.CdmaCellLocation; import android.text.TextUtils; import android.telephony.Rlog; import android.util.Log; import android.telephony.TelephonyManager; import com.android.ims.ImsManager; import com.android.internal.telephony.Call; import com.android.internal.telephony.CallStateException; import com.android.internal.telephony.CallTracker; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Connection; import com.android.internal.telephony.IccPhoneBookInterfaceManager; import com.android.internal.telephony.MccTable; import com.android.internal.telephony.MmiCode; import com.android.internal.telephony.PhoneBase; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.PhoneNotifier; import com.android.internal.telephony.PhoneProxy; import com.android.internal.telephony.PhoneSubInfo; import com.android.internal.telephony.ServiceStateTracker; import com.android.internal.telephony.SubscriptionController; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyProperties; import com.android.internal.telephony.UUSInfo; import com.android.internal.telephony.dataconnection.DcTracker; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.uicc.IccException; import com.android.internal.telephony.uicc.IccRecords; import com.android.internal.telephony.uicc.RuimRecords; 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 CDMAPhone extends PhoneBase { static final String LOG_TAG = "CDMAPhone"; private static final boolean DBG = true; private static final boolean VDBG = false; /* STOP SHIP if true */ // 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"; private String mVmNumber = null; static final int RESTART_ECM_TIMER = 0; // restart Ecm timer static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer // Instance Variables CdmaCallTracker mCT; CdmaServiceStateTracker mSST; CdmaSubscriptionSourceManager mCdmaSSM; ArrayList mPendingMmis = new ArrayList(); RuimPhoneBookInterfaceManager mRuimPhoneBookInterfaceManager; int mCdmaSubscriptionSource = CdmaSubscriptionSourceManager.SUBSCRIPTION_SOURCE_UNKNOWN; PhoneSubInfo mSubInfo; EriManager mEriManager; WakeLock mWakeLock; // mEriFileLoadedRegistrants are informed after the ERI text has been loaded private final RegistrantList mEriFileLoadedRegistrants = new RegistrantList(); // mEcmTimerResetRegistrants are informed after Ecm timer is canceled or re-started private final RegistrantList mEcmTimerResetRegistrants = new RegistrantList(); // mEcmExitRespRegistrant is informed after the phone has been exited //the emergency callback mode //keep track of if phone is in emergency callback mode protected boolean mIsPhoneInEcmState; private Registrant mEcmExitRespRegistrant; protected String mImei; protected String mImeiSv; private String mEsn; private String mMeid; // string to define how the carrier specifies its own ota sp number protected 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(); } }; Registrant mPostDialHandler; static String PROPERTY_CDMA_HOME_OPERATOR_NUMERIC = "ro.cdma.home.operator.numeric"; public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId) { super("CDMA", notifier, context, ci, false, phoneId); initSstIcc(); init(context, notifier); } protected void initSstIcc() { mSST = new CdmaServiceStateTracker(this); } protected void init(Context context, PhoneNotifier notifier) { mCi.setPhoneType(PhoneConstants.PHONE_TYPE_CDMA); mCT = new CdmaCallTracker(this); mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(context, mCi, this, EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null); mDcTracker = new DcTracker(this); mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this); mSubInfo = new PhoneSubInfo(this); mEriManager = new EriManager(this, context, EriManager.ERI_FROM_XML); 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); mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null); mCi.setEmergencyCallbackMode(this, EVENT_EMERGENCY_CALLBACK_MODE_ENTER, null); mCi.registerForExitEmergencyCallbackMode(this, EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE, null); PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,LOG_TAG); TelephonyManager tm = TelephonyManager.from(mContext); //Change the system setting tm.setPhoneType(getPhoneId(), PhoneConstants.PHONE_TYPE_CDMA); // This is needed to handle phone process crashes String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); mIsPhoneInEcmState = inEcm.equals("true"); if (mIsPhoneInEcmState) { // Send a message which will invoke handleExitEmergencyCallbackMode mCi.exitEmergencyCallbackMode(obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE)); } // get the string that specifies the carrier OTA Sp number mCarrierOtaSpNumSchema = tm.getOtaSpNumberSchemaForPhone(getPhoneId(), ""); // 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); log("init: operatorAlpha='" + operatorAlpha + "' operatorNumeric='" + operatorNumeric + "'"); if (mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP) == null) { log("init: APP_FAM_3GPP == NULL"); if (!TextUtils.isEmpty(operatorAlpha)) { log("init: set 'gsm.sim.operator.alpha' to operator='" + operatorAlpha + "'"); tm.setSimOperatorNameForPhone(mPhoneId, operatorAlpha); } if (!TextUtils.isEmpty(operatorNumeric)) { log("init: set 'gsm.sim.operator.numeric' to operator='" + operatorNumeric + "'"); log("update icc_operator_numeric=" + operatorNumeric); tm.setSimOperatorNumericForPhone(mPhoneId, operatorNumeric); SubscriptionController.getInstance().setMccMnc(operatorNumeric, getSubId()); } setIsoCountryProperty(operatorNumeric); } // Sets current entry in the telephony carrier table updateCurrentCarrierInProvider(operatorNumeric); } @Override public void dispose() { synchronized(PhoneProxy.lockForRadioTechnologyChange) { super.dispose(); log("dispose"); //Unregister from all former registered events unregisterForRuimRecordEvents(); mCi.unregisterForAvailable(this); //EVENT_RADIO_AVAILABLE mCi.unregisterForOffOrNotAvailable(this); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE mCi.unregisterForOn(this); //EVENT_RADIO_ON mSST.unregisterForNetworkAttached(this); //EVENT_REGISTERED_TO_NETWORK mCi.unSetOnSuppServiceNotification(this); mCi.unregisterForExitEmergencyCallbackMode(this); removeCallbacks(mExitEcmRunnable); mPendingMmis.clear(); //Force all referenced classes to unregister their former registered events mCT.dispose(); mDcTracker.dispose(); mSST.dispose(); mCdmaSSM.dispose(this); mRuimPhoneBookInterfaceManager.dispose(); mSubInfo.dispose(); mEriManager.dispose(); } } @Override public void removeReferences() { log("removeReferences"); mRuimPhoneBookInterfaceManager = null; mSubInfo = null; mCT = null; mSST = null; mEriManager = null; mExitEcmRunnable = null; super.removeReferences(); } @Override protected void finalize() { if(DBG) Rlog.d(LOG_TAG, "CDMAPhone finalized"); if (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 CallTracker getCallTracker() { return mCT; } @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 ServiceStateTracker getServiceStateTracker() { return mSST; } @Override public int getPhoneType() { return PhoneConstants.PHONE_TYPE_CDMA; } @Override public boolean canTransfer() { Rlog.e(LOG_TAG, "canTransfer: not possible in CDMA"); return false; } @Override public Call getRingingCall() { ImsPhone imPhone = mImsPhone; if ( mCT.mRingingCall != null && mCT.mRingingCall.isRinging() ) { return mCT.mRingingCall; } else if ( imPhone != null ) { return imPhone.getRingingCall(); } return mCT.mRingingCall; } @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 conference() { if (mImsPhone != null && mImsPhone.canConference()) { log("conference() - delegated to IMS phone"); mImsPhone.conference(); return; } // three way calls in CDMA will be handled by feature codes Rlog.e(LOG_TAG, "conference: not possible in CDMA"); } @Override public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) { mCi.setPreferredVoicePrivacy(enable, onComplete); } @Override public void getEnhancedVoicePrivacy(Message onComplete) { mCi.getPreferredVoicePrivacy(onComplete); } @Override public void clearDisconnected() { mCT.clearDisconnected(); } @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; } @Override public Connection dial (String dialString, int videoState) throws CallStateException { ImsPhone imsPhone = mImsPhone; boolean imsUseEnabled = ImsManager.isVolteEnabledByPlatform(mContext) && ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext) && ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext); if (!imsUseEnabled) { Rlog.w(LOG_TAG, "IMS is disabled: forced to CS"); } if (DBG) { Rlog.d(LOG_TAG, "imsUseEnabled=" + imsUseEnabled + ", imsPhone=" + imsPhone + ", imsPhone.isVolteEnabled()=" + ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A") + ", imsPhone.getServiceState().getState()=" + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A")); } if (imsUseEnabled && imsPhone != null && imsPhone.isVolteEnabled() && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE && !PhoneNumberUtils.isEmergencyNumber(dialString)) || (PhoneNumberUtils.isEmergencyNumber(dialString) && mContext.getResources().getBoolean( com.android.internal.R.bool.useImsAlwaysForEmergencyCall))) ) { try { if (DBG) Rlog.d(LOG_TAG, "Trying IMS PS call"); return imsPhone.dial(dialString, videoState); } catch (CallStateException e) { if (DBG) Rlog.d(LOG_TAG, "IMS PS call exception " + e + "imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone); if (!ImsPhone.CS_FALLBACK.equals(e.getMessage())) { CallStateException ce = new CallStateException(e.getMessage()); ce.setStackTrace(e.getStackTrace()); throw ce; } } } if (DBG) Rlog.d(LOG_TAG, "Trying (non-IMS) CS call"); return dialInternal(dialString, null, videoState); } @Override protected Connection dialInternal (String dialString, UUSInfo uusInfo, int videoState) throws CallStateException { // Need to make sure dialString gets parsed properly String newDialString = PhoneNumberUtils.stripSeparators(dialString); return mCT.dial(newDialString); } @Override public Connection dial(String dialString, UUSInfo uusInfo, int videoState) throws CallStateException { throw new CallStateException("Sending UUS information NOT supported in CDMA!"); } @Override public List getPendingMmiCodes() { return mPendingMmis; } @Override public void registerForSuppServiceNotification( Handler h, int what, Object obj) { Rlog.e(LOG_TAG, "method registerForSuppServiceNotification is NOT supported in CDMA!"); } @Override public CdmaCall getBackgroundCall() { return mCT.mBackgroundCall; } @Override public boolean handleInCallMmiCommands(String dialString) { Rlog.e(LOG_TAG, "method handleInCallMmiCommands is NOT supported in CDMA!"); return false; } boolean isInCall() { CdmaCall.State foregroundCallState = getForegroundCall().getState(); CdmaCall.State backgroundCallState = getBackgroundCall().getState(); CdmaCall.State ringingCallState = getRingingCall().getState(); return (foregroundCallState.isAlive() || backgroundCallState.isAlive() || ringingCallState .isAlive()); } @Override public void unregisterForSuppServiceNotification(Handler h) { Rlog.e(LOG_TAG, "method unregisterForSuppServiceNotification is NOT supported in CDMA!"); } @Override public void acceptCall(int videoState) throws CallStateException { ImsPhone 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 (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 getLine1Number() { return mSST.getMdnNumber(); } @Override public String getCdmaPrlVersion(){ return mSST.getPrlVersion(); } @Override public String getCdmaMin() { return mSST.getCdmaMin(); } @Override public boolean isMinInfoReady() { return mSST.isMinInfoReady(); } @Override public void getCallWaiting(Message onComplete) { mCi.queryCallWaiting(CommandsInterface.SERVICE_CLASS_VOICE, onComplete); } @Override public void setRadioPower(boolean power) { mSST.setRadioPower(power); } @Override public String getEsn() { return mEsn; } @Override public String getMeid() { return mMeid; } @Override public String getNai() { IccRecords r = mIccRecords.get(); if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { Rlog.v(LOG_TAG, "IccRecords is " + r); } return (r != null) ? r.getNAI() : null; } //returns MEID or ESN in CDMA @Override public String getDeviceId() { String id = getMeid(); if ((id == null) || id.matches("^0*$")) { Rlog.d(LOG_TAG, "getDeviceId(): MEID is not initialized use ESN"); id = getEsn(); } return id; } @Override public String getDeviceSvn() { Rlog.d(LOG_TAG, "getDeviceSvn(): return 0"); return "0"; } @Override public String getSubscriberId() { return mSST.getImsi(); } @Override public String getGroupIdLevel1() { Rlog.e(LOG_TAG, "GID1 is not available in CDMA"); return null; } @Override public String getImei() { Rlog.e(LOG_TAG, "getImei() called for CDMAPhone"); return mImei; } @Override public boolean canConference() { if (mImsPhone != null && mImsPhone.canConference()) { return true; } Rlog.e(LOG_TAG, "canConference: not possible in CDMA"); return false; } @Override public CellLocation getCellLocation() { CdmaCellLocation loc = 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 CdmaCall getForegroundCall() { return mCT.mForegroundCall; } @Override public void setOnPostDialCharacter(Handler h, int what, Object obj) { mPostDialHandler = new Registrant(h, what, obj); } @Override public boolean handlePinMmi(String dialString) { CdmaMmiCode mmi = CdmaMmiCode.newFromDialString(dialString, this, mUiccApplication.get()); if (mmi == null) { Rlog.e(LOG_TAG, "Mmi is NULL!"); return false; } else if (mmi.isPinPukCommand()) { mPendingMmis.add(mmi); mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); mmi.processCode(); return true; } Rlog.e(LOG_TAG, "Unrecognized mmi!"); return false; } /** * Removes the given MMI from the pending list and notifies registrants that * it is complete. * * @param mmi MMI that is done */ void onMMIDone(CdmaMmiCode mmi) { /* * Only notify complete if it's on the pending list. Otherwise, it's * already been handled (eg, previously canceled). */ if (mPendingMmis.remove(mmi)) { mMmiCompleteRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); } } @Override public boolean setLine1Number(String alphaTag, String number, final Message onComplete) { Rlog.e(LOG_TAG, "setLine1Number: not possible in CDMA"); return false; } @Override public void setCallWaiting(boolean enable, Message onComplete) { Rlog.e(LOG_TAG, "method setCallWaiting is NOT supported in CDMA!"); } @Override public void updateServiceLocation() { mSST.enableSingleLocationUpdate(); } @Override public void setDataRoamingEnabled(boolean enable) { mDcTracker.setDataOnRoamingEnabled(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 void getNeighboringCids(Message response) { /* * 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 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) { // If we're out of service, open TCP sockets may still work // but no data will flow ret = PhoneConstants.DataState.DISCONNECTED; } else if (mDcTracker.isApnTypeEnabled(apnType) == false || mDcTracker.isApnTypeActive(apnType) == false) { ret = PhoneConstants.DataState.DISCONNECTED; } else { 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; } } log("getDataConnectionState apnType=" + apnType + " ret=" + ret); return ret; } @Override public void sendUssdResponse(String ussdMessge) { Rlog.e(LOG_TAG, "sendUssdResponse: not possible in CDMA"); } @Override public void sendDtmf(char c) { if (!PhoneNumberUtils.is12Key(c)) { Rlog.e(LOG_TAG, "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)) { Rlog.e(LOG_TAG, "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) { 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 getAvailableNetworks(Message response) { Rlog.e(LOG_TAG, "getAvailableNetworks: not possible in CDMA"); } @Override public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) { Rlog.e(LOG_TAG, "setOutgoingCallerIdDisplay: not possible in CDMA"); } @Override public void enableLocationUpdates() { mSST.enableLocationUpdates(); } @Override public void disableLocationUpdates() { mSST.disableLocationUpdates(); } @Override public void getDataCallList(Message response) { mCi.getDataCallList(response); } @Override public boolean getDataRoamingEnabled() { return mDcTracker.getDataOnRoamingEnabled(); } @Override public void setDataEnabled(boolean enable) { mDcTracker.setDataEnabled(enable); } @Override public boolean getDataEnabled() { return mDcTracker.getDataEnabled(); } @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); } } @Override public String getVoiceMailNumber() { String number = null; SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); number = sp.getString(VM_NUMBER_CDMA + getPhoneId(), null); if (TextUtils.isEmpty(number)) { String[] listArray = getContext().getResources() .getStringArray(com.android.internal.R.array.config_default_vm_number); if (listArray != null && listArray.length > 0) { for (int i=0; i 0) { if (defaultVMNumberArray.length == 1) { number = defaultVMNumberArray[0]; } else if (defaultVMNumberArray.length == 2 && !TextUtils.isEmpty(defaultVMNumberArray[1]) && defaultVMNumberArray[1].equalsIgnoreCase(getGroupIdLevel1())) { number = defaultVMNumberArray[0]; break; } } } } } } if (TextUtils.isEmpty(number)) { // Read platform settings for dynamic voicemail number if (getContext().getResources().getBoolean(com.android.internal .R.bool.config_telephony_use_own_number_for_voicemail)) { number = getLine1Number(); } else { number = "*86"; } } return number; } // pending voice mail count updated after phone creation private void updateVoiceMail() { setVoiceMessageCount(getStoredVoiceMessageCount()); } @Override public String getVoiceMailAlphaTag() { // TODO: Where can we get this value has to be clarified with QC. String ret = "";//TODO: Remove = "", if we know where to get this value. //ret = mSIMRecords.getVoiceMailAlphaTag(); if (ret == null || ret.length() == 0) { return mContext.getText( com.android.internal.R.string.defaultVoiceMailAlphaTag).toString(); } return ret; } @Override public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) { Rlog.e(LOG_TAG, "getCallForwardingOption: not possible in CDMA"); } @Override public void setCallForwardingOption(int commandInterfaceCFAction, int commandInterfaceCFReason, String dialingNumber, int timerSeconds, Message onComplete) { Rlog.e(LOG_TAG, "setCallForwardingOption: not possible in CDMA"); } @Override public void getOutgoingCallerIdDisplay(Message onComplete) { Rlog.e(LOG_TAG, "getOutgoingCallerIdDisplay: not possible in CDMA"); } @Override public boolean getCallForwardingIndicator() { Rlog.e(LOG_TAG, "getCallForwardingIndicator: not possible in CDMA"); return false; } @Override public void explicitCallTransfer() { Rlog.e(LOG_TAG, "explicitCallTransfer: not possible in CDMA"); } @Override public String getLine1AlphaTag() { Rlog.e(LOG_TAG, "getLine1AlphaTag: not possible in CDMA"); return null; } /** * Notify any interested party of a Phone state change * {@link com.android.internal.telephony.PhoneConstants.State} */ /*package*/ 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. */ /*package*/ void notifyPreciseCallStateChanged() { /* we'd love it if this was package-scoped*/ super.notifyPreciseCallStateChangedP(); } void notifyServiceStateChanged(ServiceState ss) { super.notifyServiceStateChangedP(ss); } void notifyLocationChanged() { mNotifier.notifyCellLocation(this); } public void notifyNewRingingConnection(Connection c) { super.notifyNewRingingConnectionP(c); } /*package*/ void notifyDisconnect(Connection cn) { mDisconnectRegistrants.notifyResult(cn); mNotifier.notifyDisconnectCause(cn.getDisconnectCause(), cn.getPreciseDisconnectCause()); } void notifyUnknownConnection(Connection connection) { mUnknownConnectionRegistrants.notifyResult(connection); } @Override public boolean isInEmergencyCall() { return mCT.isInEmergencyCall(); } @Override public boolean isInEcm() { return mIsPhoneInEcmState; } void sendEmergencyCallbackModeChange(){ //Send an Intent Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, mIsPhoneInEcmState); SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId()); ActivityManagerNative.broadcastStickyIntent(intent,null,UserHandle.USER_ALL); if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange"); } @Override public void exitEmergencyCallbackMode() { if (mWakeLock.isHeld()) { mWakeLock.release(); } // Send a message which will invoke handleExitEmergencyCallbackMode mCi.exitEmergencyCallbackMode(obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE)); } private void handleEnterEmergencyCallbackMode(Message msg) { if (DBG) { Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= " + mIsPhoneInEcmState); } // if phone is not in Ecm mode, and it's changed to Ecm mode if (mIsPhoneInEcmState == false) { mIsPhoneInEcmState = true; // notify change sendEmergencyCallbackModeChange(); setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "true"); // 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(); } } private void handleExitEmergencyCallbackMode(Message msg) { AsyncResult ar = (AsyncResult)msg.obj; if (DBG) { Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode,ar.exception , mIsPhoneInEcmState " + ar.exception + mIsPhoneInEcmState); } // Remove pending exit Ecm runnable, if any removeCallbacks(mExitEcmRunnable); if (mEcmExitRespRegistrant != null) { mEcmExitRespRegistrant.notifyRegistrant(ar); } // if exiting ecm success if (ar.exception == null) { if (mIsPhoneInEcmState) { mIsPhoneInEcmState = false; setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "false"); } // send an Intent sendEmergencyCallbackModeChange(); // Re-initiate data connection mDcTracker.setInternalDataEnabled(true); } } /** * 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. */ 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); } } 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); } @Override public void handleMessage(Message msg) { AsyncResult ar; Message onComplete; // messages to be handled whether or not the phone is being destroyed // should only include messages which are being re-directed and do not use // resources of the phone being destroyed switch (msg.what) { // handle the select network completion callbacks. case EVENT_SET_NETWORK_MANUAL_COMPLETE: case EVENT_SET_NETWORK_AUTOMATIC_COMPLETE: super.handleMessage(msg); return; } if (!mIsTheCurrentActivePhone) { Rlog.e(LOG_TAG, "Received message " + msg + "[" + msg.what + "] while being destroyed. Ignoring."); return; } switch(msg.what) { case EVENT_RADIO_AVAILABLE: { mCi.getBasebandVersion(obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE)); mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE)); mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY)); } break; case EVENT_GET_BASEBAND_VERSION_DONE:{ ar = (AsyncResult)msg.obj; if (ar.exception != null) { break; } if (DBG) Rlog.d(LOG_TAG, "Baseband version: " + ar.result); TelephonyManager.from(mContext).setBasebandVersionForPhone(getPhoneId(), (String)ar.result); } 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_RUIM_RECORDS_LOADED:{ Rlog.d(LOG_TAG, "Event EVENT_RUIM_RECORDS_LOADED Received"); updateCurrentCarrierInProvider(); // Notify voicemails. log("notifyMessageWaitingChanged"); mNotifier.notifyMessageWaitingChanged(this); updateVoiceMail(); } break; case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:{ Rlog.d(LOG_TAG, "Event EVENT_RADIO_OFF_OR_NOT_AVAILABLE Received"); ImsPhone imsPhone = mImsPhone; if (imsPhone != null) { imsPhone.getServiceState().setStateOff(); } } break; case EVENT_RADIO_ON:{ Rlog.d(LOG_TAG, "Event EVENT_RADIO_ON Received"); handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource()); } break; case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:{ Rlog.d(LOG_TAG, "EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED"); handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource()); } break; case EVENT_SSN:{ Rlog.d(LOG_TAG, "Event EVENT_SSN Received"); } break; case EVENT_REGISTERED_TO_NETWORK:{ Rlog.d(LOG_TAG, "Event EVENT_REGISTERED_TO_NETWORK Received"); } break; case EVENT_NV_READY:{ Rlog.d(LOG_TAG, "Event EVENT_NV_READY Received"); prepareEri(); // Notify voicemails. log("notifyMessageWaitingChanged"); mNotifier.notifyMessageWaitingChanged(this); updateVoiceMail(); } break; case EVENT_SET_VM_NUMBER_DONE:{ ar = (AsyncResult)msg.obj; if (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; default:{ super.handleMessage(msg); } } } protected UiccCardApplication getUiccCardApplication() { return mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP2); } @Override protected void onUpdateIccAvailability() { if (mUiccController == null ) { return; } UiccCardApplication newUiccApplication = getUiccCardApplication(); if (newUiccApplication == null) { log("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) { log("Removing stale icc objects."); if (mIccRecords.get() != null) { unregisterForRuimRecordEvents(); } mIccRecords.set(null); mUiccApplication.set(null); } if (newUiccApplication != null) { log("New Uicc application found"); mUiccApplication.set(newUiccApplication); mIccRecords.set(newUiccApplication.getIccRecords()); registerForRuimRecordEvents(); } } } /** * Handles the call to get the subscription source * * @param newSubscriptionSource holds the new CDMA subscription source value */ private void handleCdmaSubscriptionSource(int newSubscriptionSource) { if (newSubscriptionSource != mCdmaSubscriptionSource) { mCdmaSubscriptionSource = newSubscriptionSource; if (newSubscriptionSource == CDMA_SUBSCRIPTION_NV) { // NV is ready when subscription source is NV sendMessage(obtainMessage(EVENT_NV_READY)); } } } /** * Retrieves the PhoneSubInfo of the CDMAPhone */ @Override public PhoneSubInfo getPhoneSubInfo() { return mSubInfo; } /** * Retrieves the IccPhoneBookInterfaceManager of the CDMAPhone */ @Override public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager() { return mRuimPhoneBookInterfaceManager; } public void registerForEriFileLoaded(Handler h, int what, Object obj) { Registrant r = new Registrant (h, what, obj); mEriFileLoadedRegistrants.add(r); } public void unregisterForEriFileLoaded(Handler h) { mEriFileLoadedRegistrants.remove(h); } // override for allowing access from other classes of this package /** * {@inheritDoc} */ @Override public void setSystemProperty(String property, String value) { super.setSystemProperty(property, value); } // override for allowing access from other classes of this package /** * {@inheritDoc} */ @Override public String getSystemProperty(String property, String defValue) { return super.getSystemProperty(property, defValue); } /** * 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) { Rlog.e(LOG_TAG, "[CDMAPhone] 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) { Rlog.e(LOG_TAG, "[CDMAPhone] 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) { Rlog.e(LOG_TAG, "[CDMAPhone] setCellBroadcastSmsConfig() is obsolete; use SmsManager"); response.sendToTarget(); } /** * Returns true if OTA Service Provisioning needs to be performed. */ @Override public boolean needsOtaServiceProvisioning() { return mSST.getOtasp() != ServiceStateTracker.OTASP_NOT_NEEDED; } 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; 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; } /** * 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; } /** * 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; } // Define the pattern/format for carrier specified OTASP number schema. // It separates by comma and/or whitespace. private static Pattern pOtaSpNumSchema = Pattern.compile("[,\\s]+"); /** * 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){ 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() { return getServiceState().getCdmaEriIconIndex(); } /** * Returns the CDMA ERI icon mode, * 0 - ON * 1 - FLASHING */ @Override public int getCdmaEriIconMode() { return getServiceState().getCdmaEriIconMode(); } /** * Returns the CDMA ERI text, */ @Override public String getCdmaEriText() { int roamInd = getServiceState().getCdmaRoamingIndicator(); int defRoamInd = getServiceState().getCdmaDefaultRoamingIndicator(); return mEriManager.getCdmaEriText(roamInd, defRoamInd); } /** * Store the voicemail number in preferences */ private void storeVoiceMailNumber(String number) { // Update the preference value of voicemail number SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putString(VM_NUMBER_CDMA + getPhoneId(), number); editor.apply(); } /** * Sets PROPERTY_ICC_OPERATOR_ISO_COUNTRY property * */ protected void setIsoCountryProperty(String operatorNumeric) { TelephonyManager tm = TelephonyManager.from(mContext); if (TextUtils.isEmpty(operatorNumeric)) { log("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) { loge("setIsoCountryProperty: countryCodeForMcc error", ex); } catch (StringIndexOutOfBoundsException ex) { loge("setIsoCountryProperty: countryCodeForMcc error", ex); } log("setIsoCountryProperty: set 'gsm.sim.operator.iso-country' to iso=" + iso); tm.setSimCountryIsoForPhone(mPhoneId, iso); } } /** * Sets the "current" field in the telephony provider according to the * build-time operator numeric property * * @return true for success; false otherwise. */ boolean updateCurrentCarrierInProvider(String operatorNumeric) { log("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); log("updateCurrentCarrierInProvider from system: numeric=" + operatorNumeric); getContext().getContentResolver().insert(uri, map); // Updates MCC MNC device configuration information log("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; } /** * Sets the "current" field in the telephony provider according to the SIM's operator. * Implemented in {@link CDMALTEPhone} for CDMA/LTE devices. * * @return true for success; false otherwise. */ boolean updateCurrentCarrierInProvider() { return true; } 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 log("ERI read, notify registrants"); mEriFileLoadedRegistrants.notifyRegistrants(); } } public boolean isEriFileLoaded() { return mEriManager.isEriFileLoaded(); } protected void registerForRuimRecordEvents() { IccRecords r = mIccRecords.get(); if (r == null) { return; } r.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null); } protected void unregisterForRuimRecordEvents() { IccRecords r = mIccRecords.get(); if (r == null) { return; } r.unregisterForRecordsLoaded(this); } /** * Sets the SIM voice message count * @param line 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 * This is a wrapper function for setVoiceMessageCount */ @Override public void setVoiceMessageWaiting(int line, int countWaiting) { setVoiceMessageCount(countWaiting); } protected void log(String s) { if (DBG) Rlog.d(LOG_TAG, s); } protected void loge(String s, Exception e) { if (DBG) Rlog.e(LOG_TAG, s, e); } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("CDMAPhone extends:"); super.dump(fd, pw, args); pw.println(" mVmNumber=" + mVmNumber); pw.println(" mCT=" + mCT); pw.println(" mSST=" + mSST); pw.println(" mCdmaSSM=" + mCdmaSSM); pw.println(" mPendingMmis=" + mPendingMmis); pw.println(" mRuimPhoneBookInterfaceManager=" + mRuimPhoneBookInterfaceManager); pw.println(" mCdmaSubscriptionSource=" + mCdmaSubscriptionSource); pw.println(" mSubInfo=" + mSubInfo); pw.println(" mEriManager=" + mEriManager); pw.println(" mWakeLock=" + mWakeLock); pw.println(" mIsPhoneInEcmState=" + mIsPhoneInEcmState); if (VDBG) pw.println(" mImei=" + mImei); if (VDBG) pw.println(" mImeiSv=" + mImeiSv); if (VDBG) pw.println(" mEsn=" + mEsn); if (VDBG) pw.println(" mMeid=" + mMeid); pw.println(" mCarrierOtaSpNumSchema=" + mCarrierOtaSpNumSchema); pw.println(" getCdmaEriIconIndex()=" + getCdmaEriIconIndex()); pw.println(" getCdmaEriIconMode()=" + getCdmaEriIconMode()); pw.println(" getCdmaEriText()=" + getCdmaEriText()); pw.println(" isMinInfoReady()=" + isMinInfoReady()); pw.println(" isCspPlmnEnabled()=" + isCspPlmnEnabled()); } @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( mPhoneId, iccRecords.getServiceProviderName()); } if (mSST != null) { mSST.pollState(); } } return status; } }