blob: 0fdd3db78107b8f17fa1d5ac7ecd6e9594bf14b1 [file] [log] [blame]
/*
* 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 <CdmaMmiCode> mPendingMmis = new ArrayList<CdmaMmiCode>();
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<? extends MmiCode>
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<listArray.length; i++) {
if (!TextUtils.isEmpty(listArray[i])) {
String[] defaultVMNumberArray = listArray[i].split(";");
if (defaultVMNumberArray != null && defaultVMNumberArray.length > 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;
}
}