| /* |
| * Copyright (C) 2008 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 com.android.internal.telephony.TelephonyProperties; |
| import com.android.internal.telephony.MccTable; |
| import com.android.internal.telephony.EventLogTags; |
| import com.android.internal.telephony.RILConstants; |
| |
| import android.content.Intent; |
| import android.telephony.SignalStrength; |
| import android.telephony.ServiceState; |
| import android.telephony.cdma.CdmaCellLocation; |
| import android.os.AsyncResult; |
| import android.os.Message; |
| import android.provider.Telephony.Intents; |
| |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.util.EventLog; |
| |
| import com.android.internal.telephony.gsm.GsmDataConnectionTracker; |
| |
| public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { |
| CDMALTEPhone mCdmaLtePhone; |
| |
| private ServiceState mLteSS; // The last LTE state from Voice Registration |
| private boolean mNeedToRegForSimLoaded = true; |
| |
| public CdmaLteServiceStateTracker(CDMALTEPhone phone) { |
| super(phone); |
| cm.registerForSIMReady(this, EVENT_SIM_READY, null); |
| mCdmaLtePhone = phone; |
| |
| mLteSS = new ServiceState(); |
| if (DBG) log("CdmaLteServiceStateTracker Constructors"); |
| } |
| |
| @Override |
| public void dispose() { |
| cm.unregisterForSIMReady(this); |
| super.dispose(); |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| AsyncResult ar; |
| int[] ints; |
| String[] strings; |
| switch (msg.what) { |
| case EVENT_POLL_STATE_GPRS: |
| if (DBG) log("handleMessage EVENT_POLL_STATE_GPRS"); |
| ar = (AsyncResult)msg.obj; |
| handlePollStateResult(msg.what, ar); |
| break; |
| case EVENT_SIM_READY: |
| if (DBG) log("handleMessage EVENT_SIM_READY"); |
| isSubscriptionFromRuim = false; |
| // Register SIM_RECORDS_LOADED dynamically. |
| // This is to avoid confilct with RUIM_READY scenario) |
| if (mNeedToRegForSimLoaded) { |
| phone.mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); |
| mNeedToRegForSimLoaded = false; |
| } |
| pollState(); |
| // Signal strength polling stops when radio is off. |
| queueNextSignalStrengthPoll(); |
| |
| // load ERI file |
| phone.prepareEri(); |
| break; |
| case EVENT_SIM_RECORDS_LOADED: |
| CdmaLteUiccRecords sim = (CdmaLteUiccRecords)phone.mIccRecords; |
| if ((sim != null) && sim.isProvisioned()) { |
| mMdn = sim.getMdn(); |
| mMin = sim.getMin(); |
| parseSidNid(sim.getSid(), sim.getNid()); |
| mPrlVersion = sim.getPrlVersion();; |
| mIsMinInfoReady = true; |
| updateOtaspState(); |
| } |
| // SID/NID/PRL is loaded. Poll service state |
| // again to update to the roaming state with |
| // the latest variables. |
| pollState(); |
| break; |
| default: |
| super.handleMessage(msg); |
| } |
| } |
| |
| /** |
| * Set the cdmaSS for EVENT_POLL_STATE_REGISTRATION_CDMA |
| */ |
| @Override |
| protected void setCdmaTechnology(int radioTechnology) { |
| // Called on voice registration state response. |
| // Just record new CDMA radio technology |
| newSS.setRadioTechnology(radioTechnology); |
| } |
| |
| /** |
| * Handle the result of one of the pollState()-related requests |
| */ |
| @Override |
| protected void handlePollStateResultMessage(int what, AsyncResult ar) { |
| if (what == EVENT_POLL_STATE_GPRS) { |
| if (DBG) log("handlePollStateResultMessage: EVENT_POLL_STATE_GPRS"); |
| String states[] = (String[])ar.result; |
| |
| int type = 0; |
| int regState = -1; |
| if (states.length > 0) { |
| try { |
| regState = Integer.parseInt(states[0]); |
| |
| // states[3] (if present) is the current radio technology |
| if (states.length >= 4 && states[3] != null) { |
| type = Integer.parseInt(states[3]); |
| } |
| } catch (NumberFormatException ex) { |
| loge("handlePollStateResultMessage: error parsing GprsRegistrationState: " |
| + ex); |
| } |
| } |
| |
| mLteSS.setRadioTechnology(type); |
| mLteSS.setState(regCodeToServiceState(regState)); |
| } else { |
| super.handlePollStateResultMessage(what, ar); |
| } |
| } |
| |
| @Override |
| protected void setSignalStrengthDefaultValues() { |
| // TODO Make a constructor only has boolean gsm as parameter |
| mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, |
| -1, -1, -1, SignalStrength.INVALID_SNR, -1, false); |
| } |
| |
| @Override |
| protected void pollState() { |
| pollingContext = new int[1]; |
| pollingContext[0] = 0; |
| |
| switch (cm.getRadioState()) { |
| case RADIO_UNAVAILABLE: |
| newSS.setStateOutOfService(); |
| newCellLoc.setStateInvalid(); |
| setSignalStrengthDefaultValues(); |
| mGotCountryCode = false; |
| |
| pollStateDone(); |
| break; |
| |
| case RADIO_OFF: |
| newSS.setStateOff(); |
| newCellLoc.setStateInvalid(); |
| setSignalStrengthDefaultValues(); |
| mGotCountryCode = false; |
| |
| pollStateDone(); |
| break; |
| |
| default: |
| // Issue all poll-related commands at once, then count |
| // down the responses which are allowed to arrive |
| // out-of-order. |
| |
| pollingContext[0]++; |
| // RIL_REQUEST_OPERATOR is necessary for CDMA |
| cm.getOperator(obtainMessage(EVENT_POLL_STATE_OPERATOR_CDMA, pollingContext)); |
| |
| pollingContext[0]++; |
| // RIL_REQUEST_VOICE_REGISTRATION_STATE is necessary for CDMA |
| cm.getVoiceRegistrationState(obtainMessage(EVENT_POLL_STATE_REGISTRATION_CDMA, |
| pollingContext)); |
| |
| int networkMode = android.provider.Settings.Secure.getInt(phone.getContext() |
| .getContentResolver(), |
| android.provider.Settings.Secure.PREFERRED_NETWORK_MODE, |
| RILConstants.PREFERRED_NETWORK_MODE); |
| if (DBG) log("pollState: network mode here is = " + networkMode); |
| if ((networkMode == RILConstants.NETWORK_MODE_GLOBAL) |
| || (networkMode == RILConstants.NETWORK_MODE_LTE_ONLY)) { |
| pollingContext[0]++; |
| // RIL_REQUEST_DATA_REGISTRATION_STATE |
| cm.getDataRegistrationState(obtainMessage(EVENT_POLL_STATE_GPRS, |
| pollingContext)); |
| } |
| break; |
| } |
| } |
| |
| @Override |
| protected void pollStateDone() { |
| // determine data NetworkType from both LET and CDMA SS |
| if (mLteSS.getState() == ServiceState.STATE_IN_SERVICE) { |
| //in LTE service |
| newNetworkType = mLteSS.getRadioTechnology(); |
| mNewDataConnectionState = mLteSS.getState(); |
| newSS.setRadioTechnology(newNetworkType); |
| log("pollStateDone LTE/eHRPD STATE_IN_SERVICE newNetworkType = " + newNetworkType); |
| } else { |
| // LTE out of service, get CDMA Service State |
| newNetworkType = newSS.getRadioTechnology(); |
| mNewDataConnectionState = radioTechnologyToDataServiceState(newNetworkType); |
| log("pollStateDone CDMA STATE_IN_SERVICE newNetworkType = " + newNetworkType + |
| " mNewDataConnectionState = " + mNewDataConnectionState); |
| } |
| |
| // TODO: Add proper support for LTE Only, we should be looking at |
| // the preferred network mode, to know when newSS state should |
| // be coming from mLteSs state. This was needed to pass a VZW |
| // LTE Only test. |
| // |
| // If CDMA service is OOS, double check if the device is running with LTE only |
| // mode. If that is the case, derive the service state from LTE side. |
| // To set in LTE only mode, sqlite3 /data/data/com.android.providers.settings/ |
| // databases/settings.db "update secure set value='11' where name='preferred_network_mode'" |
| if (newSS.getState() == ServiceState.STATE_OUT_OF_SERVICE) { |
| int networkMode = android.provider.Settings.Secure.getInt(phone.getContext() |
| .getContentResolver(), |
| android.provider.Settings.Secure.PREFERRED_NETWORK_MODE, |
| RILConstants.PREFERRED_NETWORK_MODE); |
| if (networkMode == RILConstants.NETWORK_MODE_LTE_ONLY) { |
| if (DBG) log("pollState: LTE Only mode"); |
| newSS.setState(mLteSS.getState()); |
| } |
| } |
| |
| if (DBG) log("pollStateDone: oldSS=[" + ss + "] newSS=[" + newSS + "]"); |
| |
| boolean hasRegistered = ss.getState() != ServiceState.STATE_IN_SERVICE |
| && newSS.getState() == ServiceState.STATE_IN_SERVICE; |
| |
| boolean hasDeregistered = ss.getState() == ServiceState.STATE_IN_SERVICE |
| && newSS.getState() != ServiceState.STATE_IN_SERVICE; |
| |
| boolean hasCdmaDataConnectionAttached = |
| mDataConnectionState != ServiceState.STATE_IN_SERVICE |
| && mNewDataConnectionState == ServiceState.STATE_IN_SERVICE; |
| |
| boolean hasCdmaDataConnectionDetached = |
| mDataConnectionState == ServiceState.STATE_IN_SERVICE |
| && mNewDataConnectionState != ServiceState.STATE_IN_SERVICE; |
| |
| boolean hasCdmaDataConnectionChanged = |
| mDataConnectionState != mNewDataConnectionState; |
| |
| boolean hasNetworkTypeChanged = networkType != newNetworkType; |
| |
| boolean hasChanged = !newSS.equals(ss); |
| |
| boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming(); |
| |
| boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming(); |
| |
| boolean hasLocationChanged = !newCellLoc.equals(cellLoc); |
| |
| boolean has4gHandoff = |
| mNewDataConnectionState == ServiceState.STATE_IN_SERVICE && |
| (((networkType == ServiceState.RADIO_TECHNOLOGY_LTE) && |
| (newNetworkType == ServiceState.RADIO_TECHNOLOGY_EHRPD)) || |
| ((networkType == ServiceState.RADIO_TECHNOLOGY_EHRPD) && |
| (newNetworkType == ServiceState.RADIO_TECHNOLOGY_LTE))); |
| |
| boolean hasMultiApnSupport = |
| (((newNetworkType == ServiceState.RADIO_TECHNOLOGY_LTE) || |
| (newNetworkType == ServiceState.RADIO_TECHNOLOGY_EHRPD)) && |
| ((networkType != ServiceState.RADIO_TECHNOLOGY_LTE) && |
| (networkType != ServiceState.RADIO_TECHNOLOGY_EHRPD))); |
| |
| boolean hasLostMultiApnSupport = |
| ((newNetworkType >= ServiceState.RADIO_TECHNOLOGY_IS95A) && |
| (newNetworkType <= ServiceState.RADIO_TECHNOLOGY_EVDO_A)); |
| |
| if (DBG) { |
| log("pollStateDone:" |
| + " hasRegistered=" + hasRegistered |
| + " hasDeegistered=" + hasDeregistered |
| + " hasCdmaDataConnectionAttached=" + hasCdmaDataConnectionAttached |
| + " hasCdmaDataConnectionDetached=" + hasCdmaDataConnectionDetached |
| + " hasCdmaDataConnectionChanged=" + hasCdmaDataConnectionChanged |
| + " hasNetworkTypeChanged = " + hasNetworkTypeChanged |
| + " hasChanged=" + hasChanged |
| + " hasRoamingOn=" + hasRoamingOn |
| + " hasRoamingOff=" + hasRoamingOff |
| + " hasLocationChanged=" + hasLocationChanged |
| + " has4gHandoff = " + has4gHandoff |
| + " hasMultiApnSupport=" + hasMultiApnSupport |
| + " hasLostMultiApnSupport=" + hasLostMultiApnSupport); |
| } |
| // Add an event log when connection state changes |
| if (ss.getState() != newSS.getState() |
| || mDataConnectionState != mNewDataConnectionState) { |
| EventLog.writeEvent(EventLogTags.CDMA_SERVICE_STATE_CHANGE, ss.getState(), |
| mDataConnectionState, newSS.getState(), mNewDataConnectionState); |
| } |
| |
| ServiceState tss; |
| tss = ss; |
| ss = newSS; |
| newSS = tss; |
| // clean slate for next time |
| newSS.setStateOutOfService(); |
| mLteSS.setStateOutOfService(); |
| |
| if ((hasMultiApnSupport) |
| && (phone.mDataConnectionTracker instanceof CdmaDataConnectionTracker)) { |
| if (DBG) log("GsmDataConnectionTracker Created"); |
| phone.mDataConnectionTracker.dispose(); |
| phone.mDataConnectionTracker = new GsmDataConnectionTracker(mCdmaLtePhone); |
| } |
| |
| if ((hasLostMultiApnSupport) |
| && (phone.mDataConnectionTracker instanceof GsmDataConnectionTracker)) { |
| if (DBG)log("GsmDataConnectionTracker disposed"); |
| phone.mDataConnectionTracker.dispose(); |
| phone.mDataConnectionTracker = new CdmaDataConnectionTracker(phone); |
| } |
| |
| CdmaCellLocation tcl = cellLoc; |
| cellLoc = newCellLoc; |
| newCellLoc = tcl; |
| |
| mDataConnectionState = mNewDataConnectionState; |
| networkType = newNetworkType; |
| |
| newSS.setStateOutOfService(); // clean slate for next time |
| |
| if (hasNetworkTypeChanged) { |
| phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE, |
| ServiceState.radioTechnologyToString(networkType)); |
| } |
| |
| if (hasRegistered) { |
| mNetworkAttachedRegistrants.notifyRegistrants(); |
| } |
| |
| if (hasChanged) { |
| if (phone.isEriFileLoaded()) { |
| String eriText; |
| // Now the CDMAPhone sees the new ServiceState so it can get the |
| // new ERI text |
| if (ss.getState() == ServiceState.STATE_IN_SERVICE) { |
| eriText = phone.getCdmaEriText(); |
| } else { |
| // Note that ServiceState.STATE_OUT_OF_SERVICE is valid used |
| // for |
| // mRegistrationState 0,2,3 and 4 |
| eriText = phone.getContext() |
| .getText(com.android.internal.R.string.roamingTextSearching).toString(); |
| } |
| ss.setOperatorAlphaLong(eriText); |
| } |
| |
| if (cm.getSimState().isSIMReady()) { |
| // SIM is found on the device. If ERI roaming is OFF and SID/NID matches |
| // one configfured in SIM, use operator name from CSIM record. |
| boolean showSpn = |
| ((CdmaLteUiccRecords)phone.mIccRecords).getCsimSpnDisplayCondition(); |
| int iconIndex = ss.getCdmaEriIconIndex(); |
| |
| if (showSpn && (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) && |
| isInHomeSidNid(ss.getSystemId(), ss.getNetworkId())) { |
| ss.setOperatorAlphaLong(phone.mIccRecords.getServiceProviderName()); |
| } |
| } |
| |
| String operatorNumeric; |
| |
| phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA, |
| ss.getOperatorAlphaLong()); |
| |
| operatorNumeric = ss.getOperatorNumeric(); |
| phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric); |
| |
| if (operatorNumeric == null) { |
| phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, ""); |
| mGotCountryCode = false; |
| } else { |
| String isoCountryCode = ""; |
| try { |
| isoCountryCode = MccTable.countryCodeForMcc(Integer.parseInt(operatorNumeric |
| .substring(0, 3))); |
| } catch (NumberFormatException ex) { |
| loge("countryCodeForMcc error" + ex); |
| } catch (StringIndexOutOfBoundsException ex) { |
| loge("countryCodeForMcc error" + ex); |
| } |
| |
| phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, |
| isoCountryCode); |
| mGotCountryCode = true; |
| if (mNeedFixZone) { |
| fixTimeZone(isoCountryCode); |
| } |
| } |
| |
| phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING, |
| ss.getRoaming() ? "true" : "false"); |
| |
| updateSpnDisplay(); |
| phone.notifyServiceStateChanged(ss); |
| } |
| |
| if (hasCdmaDataConnectionAttached || has4gHandoff) { |
| mAttachedRegistrants.notifyRegistrants(); |
| } |
| |
| if (hasCdmaDataConnectionDetached) { |
| mDetachedRegistrants.notifyRegistrants(); |
| } |
| |
| if ((hasCdmaDataConnectionChanged || hasNetworkTypeChanged)) { |
| phone.notifyDataConnection(null); |
| } |
| |
| if (hasRoamingOn) { |
| mRoamingOnRegistrants.notifyRegistrants(); |
| } |
| |
| if (hasRoamingOff) { |
| mRoamingOffRegistrants.notifyRegistrants(); |
| } |
| |
| if (hasLocationChanged) { |
| phone.notifyLocationChanged(); |
| } |
| } |
| |
| @Override |
| protected void onSignalStrengthResult(AsyncResult ar) { |
| SignalStrength oldSignalStrength = mSignalStrength; |
| |
| if (ar.exception != null) { |
| // Most likely radio is resetting/disconnected change to default |
| // values. |
| setSignalStrengthDefaultValues(); |
| } else { |
| int[] ints = (int[])ar.result; |
| |
| int lteRssi = -1; |
| int lteRsrp = -1; |
| int lteRsrq = -1; |
| int lteRssnr = SignalStrength.INVALID_SNR; |
| int lteCqi = -1; |
| |
| int offset = 2; |
| int cdmaDbm = (ints[offset] > 0) ? -ints[offset] : -120; |
| int cdmaEcio = (ints[offset + 1] > 0) ? -ints[offset + 1] : -160; |
| int evdoRssi = (ints[offset + 2] > 0) ? -ints[offset + 2] : -120; |
| int evdoEcio = (ints[offset + 3] > 0) ? -ints[offset + 3] : -1; |
| int evdoSnr = ((ints[offset + 4] > 0) && (ints[offset + 4] <= 8)) ? ints[offset + 4] |
| : -1; |
| |
| if (networkType == ServiceState.RADIO_TECHNOLOGY_LTE) { |
| lteRssi = ints[offset+5]; |
| lteRsrp = ints[offset+6]; |
| lteRsrq = ints[offset+7]; |
| lteRssnr = ints[offset+8]; |
| lteCqi = ints[offset+9]; |
| } |
| |
| if (networkType != ServiceState.RADIO_TECHNOLOGY_LTE) { |
| mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio, evdoRssi, evdoEcio, |
| evdoSnr, false); |
| } else { |
| mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio, evdoRssi, evdoEcio, |
| evdoSnr, lteRssi, lteRsrp, lteRsrq, lteRssnr, lteCqi, true); |
| } |
| } |
| |
| try { |
| phone.notifySignalStrength(); |
| } catch (NullPointerException ex) { |
| loge("onSignalStrengthResult() Phone already destroyed: " + ex |
| + "SignalStrength not notified"); |
| } |
| } |
| |
| @Override |
| public boolean isConcurrentVoiceAndDataAllowed() { |
| // Note: it needs to be confirmed which CDMA network types |
| // can support voice and data calls concurrently. |
| // For the time-being, the return value will be false. |
| return (networkType == ServiceState.RADIO_TECHNOLOGY_LTE); |
| } |
| |
| /** |
| * Check whether the specified SID and NID pair appears in the HOME SID/NID list |
| * read from NV or SIM. |
| * |
| * @return true if provided sid/nid pair belongs to operator's home network. |
| */ |
| private boolean isInHomeSidNid(int sid, int nid) { |
| // if SID/NID is not available, assume this is home network. |
| if (isSidsAllZeros()) return true; |
| |
| // length of SID/NID shold be same |
| if (mHomeSystemId.length != mHomeNetworkId.length) return true; |
| |
| if (sid == 0) return true; |
| |
| for (int i = 0; i < mHomeSystemId.length; i++) { |
| // Use SID only if NID is a reserved value. |
| // SID 0 and NID 0 and 65535 are reserved. (C.0005 2.6.5.2) |
| if ((mHomeSystemId[i] == sid) && |
| ((mHomeNetworkId[i] == 0) || (mHomeNetworkId[i] == 65535) || |
| (nid == 0) || (nid == 65535) || (mHomeNetworkId[i] == nid))) { |
| return true; |
| } |
| } |
| // SID/NID are not in the list. So device is not in home network |
| return false; |
| } |
| |
| @Override |
| protected void log(String s) { |
| Log.d(LOG_TAG, "[CdmaLteSST] " + s); |
| } |
| |
| @Override |
| protected void loge(String s) { |
| Log.e(LOG_TAG, "[CdmaLteSST] " + s); |
| } |
| } |