1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.internal.telephony.cdma; 18 19import com.android.internal.telephony.TelephonyProperties; 20import com.android.internal.telephony.MccTable; 21import com.android.internal.telephony.EventLogTags; 22import com.android.internal.telephony.RILConstants; 23 24import android.content.Intent; 25import android.telephony.SignalStrength; 26import android.telephony.ServiceState; 27import android.telephony.cdma.CdmaCellLocation; 28import android.os.AsyncResult; 29import android.os.Message; 30import android.provider.Telephony.Intents; 31 32import android.text.TextUtils; 33import android.util.Log; 34import android.util.EventLog; 35 36import com.android.internal.telephony.gsm.GsmDataConnectionTracker; 37 38public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { 39 CDMALTEPhone mCdmaLtePhone; 40 41 private ServiceState mLteSS; // The last LTE state from Voice Registration 42 private boolean mNeedToRegForSimLoaded = true; 43 44 public CdmaLteServiceStateTracker(CDMALTEPhone phone) { 45 super(phone); 46 cm.registerForSIMReady(this, EVENT_SIM_READY, null); 47 mCdmaLtePhone = phone; 48 49 mLteSS = new ServiceState(); 50 if (DBG) log("CdmaLteServiceStateTracker Constructors"); 51 } 52 53 @Override 54 public void dispose() { 55 cm.unregisterForSIMReady(this); 56 super.dispose(); 57 } 58 59 @Override 60 public void handleMessage(Message msg) { 61 AsyncResult ar; 62 int[] ints; 63 String[] strings; 64 switch (msg.what) { 65 case EVENT_POLL_STATE_GPRS: 66 if (DBG) log("handleMessage EVENT_POLL_STATE_GPRS"); 67 ar = (AsyncResult)msg.obj; 68 handlePollStateResult(msg.what, ar); 69 break; 70 case EVENT_SIM_READY: 71 if (DBG) log("handleMessage EVENT_SIM_READY"); 72 isSubscriptionFromRuim = false; 73 // Register SIM_RECORDS_LOADED dynamically. 74 // This is to avoid confilct with RUIM_READY scenario) 75 if (mNeedToRegForSimLoaded) { 76 phone.mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); 77 mNeedToRegForSimLoaded = false; 78 } 79 pollState(); 80 // Signal strength polling stops when radio is off. 81 queueNextSignalStrengthPoll(); 82 83 // load ERI file 84 phone.prepareEri(); 85 break; 86 case EVENT_SIM_RECORDS_LOADED: 87 CdmaLteUiccRecords sim = (CdmaLteUiccRecords)phone.mIccRecords; 88 if ((sim != null) && sim.isProvisioned()) { 89 mMdn = sim.getMdn(); 90 mMin = sim.getMin(); 91 parseSidNid(sim.getSid(), sim.getNid()); 92 mPrlVersion = sim.getPrlVersion();; 93 mIsMinInfoReady = true; 94 updateOtaspState(); 95 } 96 // SID/NID/PRL is loaded. Poll service state 97 // again to update to the roaming state with 98 // the latest variables. 99 pollState(); 100 break; 101 default: 102 super.handleMessage(msg); 103 } 104 } 105 106 /** 107 * Set the cdmaSS for EVENT_POLL_STATE_REGISTRATION_CDMA 108 */ 109 @Override 110 protected void setCdmaTechnology(int radioTechnology) { 111 // Called on voice registration state response. 112 // Just record new CDMA radio technology 113 newSS.setRadioTechnology(radioTechnology); 114 } 115 116 /** 117 * Handle the result of one of the pollState()-related requests 118 */ 119 @Override 120 protected void handlePollStateResultMessage(int what, AsyncResult ar) { 121 if (what == EVENT_POLL_STATE_GPRS) { 122 if (DBG) log("handlePollStateResultMessage: EVENT_POLL_STATE_GPRS"); 123 String states[] = (String[])ar.result; 124 125 int type = 0; 126 int regState = -1; 127 if (states.length > 0) { 128 try { 129 regState = Integer.parseInt(states[0]); 130 131 // states[3] (if present) is the current radio technology 132 if (states.length >= 4 && states[3] != null) { 133 type = Integer.parseInt(states[3]); 134 } 135 } catch (NumberFormatException ex) { 136 loge("handlePollStateResultMessage: error parsing GprsRegistrationState: " 137 + ex); 138 } 139 } 140 141 mLteSS.setRadioTechnology(type); 142 mLteSS.setState(regCodeToServiceState(regState)); 143 } else { 144 super.handlePollStateResultMessage(what, ar); 145 } 146 } 147 148 @Override 149 protected void setSignalStrengthDefaultValues() { 150 mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, false); 151 } 152 153 @Override 154 protected void pollState() { 155 pollingContext = new int[1]; 156 pollingContext[0] = 0; 157 158 switch (cm.getRadioState()) { 159 case RADIO_UNAVAILABLE: 160 newSS.setStateOutOfService(); 161 newCellLoc.setStateInvalid(); 162 setSignalStrengthDefaultValues(); 163 mGotCountryCode = false; 164 165 pollStateDone(); 166 break; 167 168 case RADIO_OFF: 169 newSS.setStateOff(); 170 newCellLoc.setStateInvalid(); 171 setSignalStrengthDefaultValues(); 172 mGotCountryCode = false; 173 174 pollStateDone(); 175 break; 176 177 default: 178 // Issue all poll-related commands at once, then count 179 // down the responses which are allowed to arrive 180 // out-of-order. 181 182 pollingContext[0]++; 183 // RIL_REQUEST_OPERATOR is necessary for CDMA 184 cm.getOperator(obtainMessage(EVENT_POLL_STATE_OPERATOR_CDMA, pollingContext)); 185 186 pollingContext[0]++; 187 // RIL_REQUEST_VOICE_REGISTRATION_STATE is necessary for CDMA 188 cm.getVoiceRegistrationState(obtainMessage(EVENT_POLL_STATE_REGISTRATION_CDMA, 189 pollingContext)); 190 191 int networkMode = android.provider.Settings.Secure.getInt(phone.getContext() 192 .getContentResolver(), 193 android.provider.Settings.Secure.PREFERRED_NETWORK_MODE, 194 RILConstants.PREFERRED_NETWORK_MODE); 195 if (DBG) log("pollState: network mode here is = " + networkMode); 196 if ((networkMode == RILConstants.NETWORK_MODE_GLOBAL) 197 || (networkMode == RILConstants.NETWORK_MODE_LTE_ONLY)) { 198 pollingContext[0]++; 199 // RIL_REQUEST_DATA_REGISTRATION_STATE 200 cm.getDataRegistrationState(obtainMessage(EVENT_POLL_STATE_GPRS, 201 pollingContext)); 202 } 203 break; 204 } 205 } 206 207 @Override 208 protected void pollStateDone() { 209 // determine data NetworkType from both LET and CDMA SS 210 if (mLteSS.getState() == ServiceState.STATE_IN_SERVICE) { 211 //in LTE service 212 newNetworkType = mLteSS.getRadioTechnology(); 213 mNewDataConnectionState = mLteSS.getState(); 214 newSS.setRadioTechnology(newNetworkType); 215 log("pollStateDone LTE/eHRPD STATE_IN_SERVICE newNetworkType = " + newNetworkType); 216 } else { 217 // LTE out of service, get CDMA Service State 218 newNetworkType = newSS.getRadioTechnology(); 219 mNewDataConnectionState = radioTechnologyToDataServiceState(newNetworkType); 220 log("pollStateDone CDMA STATE_IN_SERVICE newNetworkType = " + newNetworkType + 221 " mNewDataConnectionState = " + mNewDataConnectionState); 222 } 223 224 // TODO: Add proper support for LTE Only, we should be looking at 225 // the preferred network mode, to know when newSS state should 226 // be coming from mLteSs state. This was needed to pass a VZW 227 // LTE Only test. 228 // 229 // If CDMA service is OOS, double check if the device is running with LTE only 230 // mode. If that is the case, derive the service state from LTE side. 231 // To set in LTE only mode, sqlite3 /data/data/com.android.providers.settings/ 232 // databases/settings.db "update secure set value='11' where name='preferred_network_mode'" 233 if (newSS.getState() == ServiceState.STATE_OUT_OF_SERVICE) { 234 int networkMode = android.provider.Settings.Secure.getInt(phone.getContext() 235 .getContentResolver(), 236 android.provider.Settings.Secure.PREFERRED_NETWORK_MODE, 237 RILConstants.PREFERRED_NETWORK_MODE); 238 if (networkMode == RILConstants.NETWORK_MODE_LTE_ONLY) { 239 if (DBG) log("pollState: LTE Only mode"); 240 newSS.setState(mLteSS.getState()); 241 } 242 } 243 244 if (DBG) log("pollStateDone: oldSS=[" + ss + "] newSS=[" + newSS + "]"); 245 246 boolean hasRegistered = ss.getState() != ServiceState.STATE_IN_SERVICE 247 && newSS.getState() == ServiceState.STATE_IN_SERVICE; 248 249 boolean hasDeregistered = ss.getState() == ServiceState.STATE_IN_SERVICE 250 && newSS.getState() != ServiceState.STATE_IN_SERVICE; 251 252 boolean hasCdmaDataConnectionAttached = 253 mDataConnectionState != ServiceState.STATE_IN_SERVICE 254 && mNewDataConnectionState == ServiceState.STATE_IN_SERVICE; 255 256 boolean hasCdmaDataConnectionDetached = 257 mDataConnectionState == ServiceState.STATE_IN_SERVICE 258 && mNewDataConnectionState != ServiceState.STATE_IN_SERVICE; 259 260 boolean hasCdmaDataConnectionChanged = 261 mDataConnectionState != mNewDataConnectionState; 262 263 boolean hasNetworkTypeChanged = networkType != newNetworkType; 264 265 boolean hasChanged = !newSS.equals(ss); 266 267 boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming(); 268 269 boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming(); 270 271 boolean hasLocationChanged = !newCellLoc.equals(cellLoc); 272 273 boolean has4gHandoff = 274 mNewDataConnectionState == ServiceState.STATE_IN_SERVICE && 275 (((networkType == ServiceState.RADIO_TECHNOLOGY_LTE) && 276 (newNetworkType == ServiceState.RADIO_TECHNOLOGY_EHRPD)) || 277 ((networkType == ServiceState.RADIO_TECHNOLOGY_EHRPD) && 278 (newNetworkType == ServiceState.RADIO_TECHNOLOGY_LTE))); 279 280 boolean hasMultiApnSupport = 281 (((newNetworkType == ServiceState.RADIO_TECHNOLOGY_LTE) || 282 (newNetworkType == ServiceState.RADIO_TECHNOLOGY_EHRPD)) && 283 ((networkType != ServiceState.RADIO_TECHNOLOGY_LTE) && 284 (networkType != ServiceState.RADIO_TECHNOLOGY_EHRPD))); 285 286 boolean hasLostMultiApnSupport = 287 ((newNetworkType >= ServiceState.RADIO_TECHNOLOGY_IS95A) && 288 (newNetworkType <= ServiceState.RADIO_TECHNOLOGY_EVDO_A)); 289 290 if (DBG) { 291 log("pollStateDone:" 292 + " hasRegistered=" + hasRegistered 293 + " hasDeegistered=" + hasDeregistered 294 + " hasCdmaDataConnectionAttached=" + hasCdmaDataConnectionAttached 295 + " hasCdmaDataConnectionDetached=" + hasCdmaDataConnectionDetached 296 + " hasCdmaDataConnectionChanged=" + hasCdmaDataConnectionChanged 297 + " hasNetworkTypeChanged = " + hasNetworkTypeChanged 298 + " hasChanged=" + hasChanged 299 + " hasRoamingOn=" + hasRoamingOn 300 + " hasRoamingOff=" + hasRoamingOff 301 + " hasLocationChanged=" + hasLocationChanged 302 + " has4gHandoff = " + has4gHandoff 303 + " hasMultiApnSupport=" + hasMultiApnSupport 304 + " hasLostMultiApnSupport=" + hasLostMultiApnSupport); 305 } 306 // Add an event log when connection state changes 307 if (ss.getState() != newSS.getState() 308 || mDataConnectionState != mNewDataConnectionState) { 309 EventLog.writeEvent(EventLogTags.CDMA_SERVICE_STATE_CHANGE, ss.getState(), 310 mDataConnectionState, newSS.getState(), mNewDataConnectionState); 311 } 312 313 ServiceState tss; 314 tss = ss; 315 ss = newSS; 316 newSS = tss; 317 // clean slate for next time 318 newSS.setStateOutOfService(); 319 mLteSS.setStateOutOfService(); 320 321 if ((hasMultiApnSupport) 322 && (phone.mDataConnectionTracker instanceof CdmaDataConnectionTracker)) { 323 if (DBG) log("GsmDataConnectionTracker Created"); 324 phone.mDataConnectionTracker.dispose(); 325 phone.mDataConnectionTracker = new GsmDataConnectionTracker(mCdmaLtePhone); 326 } 327 328 if ((hasLostMultiApnSupport) 329 && (phone.mDataConnectionTracker instanceof GsmDataConnectionTracker)) { 330 if (DBG)log("GsmDataConnectionTracker disposed"); 331 phone.mDataConnectionTracker.dispose(); 332 phone.mDataConnectionTracker = new CdmaDataConnectionTracker(phone); 333 } 334 335 CdmaCellLocation tcl = cellLoc; 336 cellLoc = newCellLoc; 337 newCellLoc = tcl; 338 339 mDataConnectionState = mNewDataConnectionState; 340 networkType = newNetworkType; 341 342 newSS.setStateOutOfService(); // clean slate for next time 343 344 if (hasNetworkTypeChanged) { 345 phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE, 346 ServiceState.radioTechnologyToString(networkType)); 347 } 348 349 if (hasRegistered) { 350 mNetworkAttachedRegistrants.notifyRegistrants(); 351 } 352 353 if (hasChanged) { 354 if (phone.isEriFileLoaded()) { 355 String eriText; 356 // Now the CDMAPhone sees the new ServiceState so it can get the 357 // new ERI text 358 if (ss.getState() == ServiceState.STATE_IN_SERVICE) { 359 eriText = phone.getCdmaEriText(); 360 } else { 361 // Note that ServiceState.STATE_OUT_OF_SERVICE is valid used 362 // for 363 // mRegistrationState 0,2,3 and 4 364 eriText = phone.getContext() 365 .getText(com.android.internal.R.string.roamingTextSearching).toString(); 366 } 367 ss.setOperatorAlphaLong(eriText); 368 } 369 370 if (cm.getSimState().isSIMReady()) { 371 // SIM is found on the device. If ERI roaming is OFF and SID/NID matches 372 // one configfured in SIM, use operator name from CSIM record. 373 boolean showSpn = 374 ((CdmaLteUiccRecords)phone.mIccRecords).getCsimSpnDisplayCondition(); 375 int iconIndex = ss.getCdmaEriIconIndex(); 376 377 if (showSpn && (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) && 378 isInHomeSidNid(ss.getSystemId(), ss.getNetworkId())) { 379 ss.setOperatorAlphaLong(phone.mIccRecords.getServiceProviderName()); 380 } 381 } 382 383 String operatorNumeric; 384 385 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA, 386 ss.getOperatorAlphaLong()); 387 388 operatorNumeric = ss.getOperatorNumeric(); 389 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric); 390 391 if (operatorNumeric == null) { 392 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, ""); 393 mGotCountryCode = false; 394 } else { 395 String isoCountryCode = ""; 396 try { 397 isoCountryCode = MccTable.countryCodeForMcc(Integer.parseInt(operatorNumeric 398 .substring(0, 3))); 399 } catch (NumberFormatException ex) { 400 loge("countryCodeForMcc error" + ex); 401 } catch (StringIndexOutOfBoundsException ex) { 402 loge("countryCodeForMcc error" + ex); 403 } 404 405 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, 406 isoCountryCode); 407 mGotCountryCode = true; 408 if (mNeedFixZone) { 409 fixTimeZone(isoCountryCode); 410 } 411 } 412 413 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING, 414 ss.getRoaming() ? "true" : "false"); 415 416 updateSpnDisplay(); 417 phone.notifyServiceStateChanged(ss); 418 } 419 420 if (hasCdmaDataConnectionAttached || has4gHandoff) { 421 mAttachedRegistrants.notifyRegistrants(); 422 } 423 424 if (hasCdmaDataConnectionDetached) { 425 mDetachedRegistrants.notifyRegistrants(); 426 } 427 428 if ((hasCdmaDataConnectionChanged || hasNetworkTypeChanged)) { 429 phone.notifyDataConnection(null); 430 } 431 432 if (hasRoamingOn) { 433 mRoamingOnRegistrants.notifyRegistrants(); 434 } 435 436 if (hasRoamingOff) { 437 mRoamingOffRegistrants.notifyRegistrants(); 438 } 439 440 if (hasLocationChanged) { 441 phone.notifyLocationChanged(); 442 } 443 } 444 445 @Override 446 protected void onSignalStrengthResult(AsyncResult ar) { 447 SignalStrength oldSignalStrength = mSignalStrength; 448 449 if (ar.exception != null) { 450 // Most likely radio is resetting/disconnected change to default 451 // values. 452 setSignalStrengthDefaultValues(); 453 } else { 454 int[] ints = (int[])ar.result; 455 int lteCqi = 99, lteRsrp = -1; 456 int lteRssi = 99; 457 int offset = 2; 458 int cdmaDbm = (ints[offset] > 0) ? -ints[offset] : -120; 459 int cdmaEcio = (ints[offset + 1] > 0) ? -ints[offset + 1] : -160; 460 int evdoRssi = (ints[offset + 2] > 0) ? -ints[offset + 2] : -120; 461 int evdoEcio = (ints[offset + 3] > 0) ? -ints[offset + 3] : -1; 462 int evdoSnr = ((ints[offset + 4] > 0) && (ints[offset + 4] <= 8)) ? ints[offset + 4] 463 : -1; 464 if (networkType == ServiceState.RADIO_TECHNOLOGY_LTE) { 465 lteRssi = (ints[offset + 5] >= 0) ? ints[offset + 5] : 99; 466 lteRsrp = (ints[offset + 6] < 0) ? ints[offset + 6] : -1; 467 lteCqi = (ints[offset + 7] >= 0) ? ints[offset + 7] : 99; 468 } 469 470 if (networkType != ServiceState.RADIO_TECHNOLOGY_LTE) { 471 mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio, evdoRssi, evdoEcio, 472 evdoSnr, false); 473 } else { 474 mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio, evdoRssi, evdoEcio, 475 evdoSnr, lteRssi, lteRsrp, -1, -1, lteCqi, true); 476 } 477 } 478 479 try { 480 phone.notifySignalStrength(); 481 } catch (NullPointerException ex) { 482 loge("onSignalStrengthResult() Phone already destroyed: " + ex 483 + "SignalStrength not notified"); 484 } 485 } 486 487 @Override 488 public boolean isConcurrentVoiceAndDataAllowed() { 489 // Note: it needs to be confirmed which CDMA network types 490 // can support voice and data calls concurrently. 491 // For the time-being, the return value will be false. 492 return (networkType == ServiceState.RADIO_TECHNOLOGY_LTE); 493 } 494 495 /** 496 * Check whether the specified SID and NID pair appears in the HOME SID/NID list 497 * read from NV or SIM. 498 * 499 * @return true if provided sid/nid pair belongs to operator's home network. 500 */ 501 private boolean isInHomeSidNid(int sid, int nid) { 502 // if SID/NID is not available, assume this is home network. 503 if (isSidsAllZeros()) return true; 504 505 // length of SID/NID shold be same 506 if (mHomeSystemId.length != mHomeNetworkId.length) return true; 507 508 if (sid == 0) return true; 509 510 for (int i = 0; i < mHomeSystemId.length; i++) { 511 // Use SID only if NID is a reserved value. 512 // SID 0 and NID 0 and 65535 are reserved. (C.0005 2.6.5.2) 513 if ((mHomeSystemId[i] == sid) && 514 ((mHomeNetworkId[i] == 0) || (mHomeNetworkId[i] == 65535) || 515 (nid == 0) || (nid == 65535) || (mHomeNetworkId[i] == nid))) { 516 return true; 517 } 518 } 519 // SID/NID are not in the list. So device is not in home network 520 return false; 521 } 522 523 @Override 524 protected void log(String s) { 525 Log.d(LOG_TAG, "[CdmaLteSST] " + s); 526 } 527 528 @Override 529 protected void loge(String s) { 530 Log.e(LOG_TAG, "[CdmaLteSST] " + s); 531 } 532} 533