CdmaLteServiceStateTracker.java revision 5e39519396918f9e2312d1c355a9b6889851d887
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        // TODO Make a constructor only has boolean gsm as parameter
151        mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1,
152                -1, -1, -1, SignalStrength.INVALID_SNR, -1, false);
153    }
154
155    @Override
156    protected void pollState() {
157        pollingContext = new int[1];
158        pollingContext[0] = 0;
159
160        switch (cm.getRadioState()) {
161            case RADIO_UNAVAILABLE:
162                newSS.setStateOutOfService();
163                newCellLoc.setStateInvalid();
164                setSignalStrengthDefaultValues();
165                mGotCountryCode = false;
166
167                pollStateDone();
168                break;
169
170            case RADIO_OFF:
171                newSS.setStateOff();
172                newCellLoc.setStateInvalid();
173                setSignalStrengthDefaultValues();
174                mGotCountryCode = false;
175
176                pollStateDone();
177                break;
178
179            default:
180                // Issue all poll-related commands at once, then count
181                // down the responses which are allowed to arrive
182                // out-of-order.
183
184                pollingContext[0]++;
185                // RIL_REQUEST_OPERATOR is necessary for CDMA
186                cm.getOperator(obtainMessage(EVENT_POLL_STATE_OPERATOR_CDMA, pollingContext));
187
188                pollingContext[0]++;
189                // RIL_REQUEST_VOICE_REGISTRATION_STATE is necessary for CDMA
190                cm.getVoiceRegistrationState(obtainMessage(EVENT_POLL_STATE_REGISTRATION_CDMA,
191                        pollingContext));
192
193                int networkMode = android.provider.Settings.Secure.getInt(phone.getContext()
194                        .getContentResolver(),
195                        android.provider.Settings.Secure.PREFERRED_NETWORK_MODE,
196                        RILConstants.PREFERRED_NETWORK_MODE);
197                if (DBG) log("pollState: network mode here is = " + networkMode);
198                if ((networkMode == RILConstants.NETWORK_MODE_GLOBAL)
199                        || (networkMode == RILConstants.NETWORK_MODE_LTE_ONLY)) {
200                    pollingContext[0]++;
201                    // RIL_REQUEST_DATA_REGISTRATION_STATE
202                    cm.getDataRegistrationState(obtainMessage(EVENT_POLL_STATE_GPRS,
203                                                pollingContext));
204                }
205                break;
206        }
207    }
208
209    @Override
210    protected void pollStateDone() {
211        // determine data NetworkType from both LET and CDMA SS
212        if (mLteSS.getState() == ServiceState.STATE_IN_SERVICE) {
213            //in LTE service
214            newNetworkType = mLteSS.getRadioTechnology();
215            mNewDataConnectionState = mLteSS.getState();
216            newSS.setRadioTechnology(newNetworkType);
217            log("pollStateDone LTE/eHRPD STATE_IN_SERVICE newNetworkType = " + newNetworkType);
218        } else {
219            // LTE out of service, get CDMA Service State
220            newNetworkType = newSS.getRadioTechnology();
221            mNewDataConnectionState = radioTechnologyToDataServiceState(newNetworkType);
222            log("pollStateDone CDMA STATE_IN_SERVICE newNetworkType = " + newNetworkType +
223                " mNewDataConnectionState = " + mNewDataConnectionState);
224        }
225
226        // TODO: Add proper support for LTE Only, we should be looking at
227        //       the preferred network mode, to know when newSS state should
228        //       be coming from mLteSs state. This was needed to pass a VZW
229        //       LTE Only test.
230        //
231        // If CDMA service is OOS, double check if the device is running with LTE only
232        // mode. If that is the case, derive the service state from LTE side.
233        // To set in LTE only mode, sqlite3 /data/data/com.android.providers.settings/
234        // databases/settings.db "update secure set value='11' where name='preferred_network_mode'"
235        if (newSS.getState() == ServiceState.STATE_OUT_OF_SERVICE) {
236            int networkMode = android.provider.Settings.Secure.getInt(phone.getContext()
237                                  .getContentResolver(),
238                                  android.provider.Settings.Secure.PREFERRED_NETWORK_MODE,
239                                  RILConstants.PREFERRED_NETWORK_MODE);
240            if (networkMode == RILConstants.NETWORK_MODE_LTE_ONLY) {
241                if (DBG) log("pollState: LTE Only mode");
242                newSS.setState(mLteSS.getState());
243            }
244        }
245
246        if (DBG) log("pollStateDone: oldSS=[" + ss + "] newSS=[" + newSS + "]");
247
248        boolean hasRegistered = ss.getState() != ServiceState.STATE_IN_SERVICE
249                && newSS.getState() == ServiceState.STATE_IN_SERVICE;
250
251        boolean hasDeregistered = ss.getState() == ServiceState.STATE_IN_SERVICE
252                && newSS.getState() != ServiceState.STATE_IN_SERVICE;
253
254        boolean hasCdmaDataConnectionAttached =
255            mDataConnectionState != ServiceState.STATE_IN_SERVICE
256                && mNewDataConnectionState == ServiceState.STATE_IN_SERVICE;
257
258        boolean hasCdmaDataConnectionDetached =
259            mDataConnectionState == ServiceState.STATE_IN_SERVICE
260                && mNewDataConnectionState != ServiceState.STATE_IN_SERVICE;
261
262        boolean hasCdmaDataConnectionChanged =
263            mDataConnectionState != mNewDataConnectionState;
264
265        boolean hasNetworkTypeChanged = networkType != newNetworkType;
266
267        boolean hasChanged = !newSS.equals(ss);
268
269        boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming();
270
271        boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming();
272
273        boolean hasLocationChanged = !newCellLoc.equals(cellLoc);
274
275        boolean has4gHandoff =
276                mNewDataConnectionState == ServiceState.STATE_IN_SERVICE &&
277                (((networkType == ServiceState.RADIO_TECHNOLOGY_LTE) &&
278                  (newNetworkType == ServiceState.RADIO_TECHNOLOGY_EHRPD)) ||
279                 ((networkType == ServiceState.RADIO_TECHNOLOGY_EHRPD) &&
280                  (newNetworkType == ServiceState.RADIO_TECHNOLOGY_LTE)));
281
282        boolean hasMultiApnSupport =
283                (((newNetworkType == ServiceState.RADIO_TECHNOLOGY_LTE) ||
284                  (newNetworkType == ServiceState.RADIO_TECHNOLOGY_EHRPD)) &&
285                 ((networkType != ServiceState.RADIO_TECHNOLOGY_LTE) &&
286                  (networkType != ServiceState.RADIO_TECHNOLOGY_EHRPD)));
287
288        boolean hasLostMultiApnSupport =
289            ((newNetworkType >= ServiceState.RADIO_TECHNOLOGY_IS95A) &&
290             (newNetworkType <= ServiceState.RADIO_TECHNOLOGY_EVDO_A));
291
292        if (DBG) {
293            log("pollStateDone:"
294                + " hasRegistered=" + hasRegistered
295                + " hasDeegistered=" + hasDeregistered
296                + " hasCdmaDataConnectionAttached=" + hasCdmaDataConnectionAttached
297                + " hasCdmaDataConnectionDetached=" + hasCdmaDataConnectionDetached
298                + " hasCdmaDataConnectionChanged=" + hasCdmaDataConnectionChanged
299                + " hasNetworkTypeChanged = " + hasNetworkTypeChanged
300                + " hasChanged=" + hasChanged
301                + " hasRoamingOn=" + hasRoamingOn
302                + " hasRoamingOff=" + hasRoamingOff
303                + " hasLocationChanged=" + hasLocationChanged
304                + " has4gHandoff = " + has4gHandoff
305                + " hasMultiApnSupport=" + hasMultiApnSupport
306                + " hasLostMultiApnSupport=" + hasLostMultiApnSupport);
307        }
308        // Add an event log when connection state changes
309        if (ss.getState() != newSS.getState()
310                || mDataConnectionState != mNewDataConnectionState) {
311            EventLog.writeEvent(EventLogTags.CDMA_SERVICE_STATE_CHANGE, ss.getState(),
312                    mDataConnectionState, newSS.getState(), mNewDataConnectionState);
313        }
314
315        ServiceState tss;
316        tss = ss;
317        ss = newSS;
318        newSS = tss;
319        // clean slate for next time
320        newSS.setStateOutOfService();
321        mLteSS.setStateOutOfService();
322
323        if ((hasMultiApnSupport)
324                && (phone.mDataConnectionTracker instanceof CdmaDataConnectionTracker)) {
325            if (DBG) log("GsmDataConnectionTracker Created");
326            phone.mDataConnectionTracker.dispose();
327            phone.mDataConnectionTracker = new GsmDataConnectionTracker(mCdmaLtePhone);
328        }
329
330        if ((hasLostMultiApnSupport)
331                && (phone.mDataConnectionTracker instanceof GsmDataConnectionTracker)) {
332            if (DBG)log("GsmDataConnectionTracker disposed");
333            phone.mDataConnectionTracker.dispose();
334            phone.mDataConnectionTracker = new CdmaDataConnectionTracker(phone);
335        }
336
337        CdmaCellLocation tcl = cellLoc;
338        cellLoc = newCellLoc;
339        newCellLoc = tcl;
340
341        mDataConnectionState = mNewDataConnectionState;
342        networkType = newNetworkType;
343
344        newSS.setStateOutOfService(); // clean slate for next time
345
346        if (hasNetworkTypeChanged) {
347            phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
348                    ServiceState.radioTechnologyToString(networkType));
349        }
350
351        if (hasRegistered) {
352            mNetworkAttachedRegistrants.notifyRegistrants();
353        }
354
355        if (hasChanged) {
356            if (phone.isEriFileLoaded()) {
357                String eriText;
358                // Now the CDMAPhone sees the new ServiceState so it can get the
359                // new ERI text
360                if (ss.getState() == ServiceState.STATE_IN_SERVICE) {
361                    eriText = phone.getCdmaEriText();
362                } else {
363                    // Note that ServiceState.STATE_OUT_OF_SERVICE is valid used
364                    // for
365                    // mRegistrationState 0,2,3 and 4
366                    eriText = phone.getContext()
367                            .getText(com.android.internal.R.string.roamingTextSearching).toString();
368                }
369                ss.setOperatorAlphaLong(eriText);
370            }
371
372            if (cm.getSimState().isSIMReady()) {
373                // SIM is found on the device. If ERI roaming is OFF and SID/NID matches
374                // one configfured in SIM, use operator name from CSIM record.
375                boolean showSpn =
376                    ((CdmaLteUiccRecords)phone.mIccRecords).getCsimSpnDisplayCondition();
377                int iconIndex = ss.getCdmaEriIconIndex();
378
379                if (showSpn && (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) &&
380                    isInHomeSidNid(ss.getSystemId(), ss.getNetworkId())) {
381                    ss.setOperatorAlphaLong(phone.mIccRecords.getServiceProviderName());
382                }
383            }
384
385            String operatorNumeric;
386
387            phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA,
388                    ss.getOperatorAlphaLong());
389
390            operatorNumeric = ss.getOperatorNumeric();
391            phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric);
392
393            if (operatorNumeric == null) {
394                phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
395                mGotCountryCode = false;
396            } else {
397                String isoCountryCode = "";
398                try {
399                    isoCountryCode = MccTable.countryCodeForMcc(Integer.parseInt(operatorNumeric
400                            .substring(0, 3)));
401                } catch (NumberFormatException ex) {
402                    loge("countryCodeForMcc error" + ex);
403                } catch (StringIndexOutOfBoundsException ex) {
404                    loge("countryCodeForMcc error" + ex);
405                }
406
407                phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY,
408                        isoCountryCode);
409                mGotCountryCode = true;
410                if (mNeedFixZone) {
411                    fixTimeZone(isoCountryCode);
412                }
413            }
414
415            phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
416                    ss.getRoaming() ? "true" : "false");
417
418            updateSpnDisplay();
419            phone.notifyServiceStateChanged(ss);
420        }
421
422        if (hasCdmaDataConnectionAttached || has4gHandoff) {
423            mAttachedRegistrants.notifyRegistrants();
424        }
425
426        if (hasCdmaDataConnectionDetached) {
427            mDetachedRegistrants.notifyRegistrants();
428        }
429
430        if ((hasCdmaDataConnectionChanged || hasNetworkTypeChanged)) {
431            phone.notifyDataConnection(null);
432        }
433
434        if (hasRoamingOn) {
435            mRoamingOnRegistrants.notifyRegistrants();
436        }
437
438        if (hasRoamingOff) {
439            mRoamingOffRegistrants.notifyRegistrants();
440        }
441
442        if (hasLocationChanged) {
443            phone.notifyLocationChanged();
444        }
445    }
446
447    @Override
448    protected void onSignalStrengthResult(AsyncResult ar) {
449        SignalStrength oldSignalStrength = mSignalStrength;
450
451        if (ar.exception != null) {
452            // Most likely radio is resetting/disconnected change to default
453            // values.
454            setSignalStrengthDefaultValues();
455        } else {
456            int[] ints = (int[])ar.result;
457
458            int lteRssi = -1;
459            int lteRsrp = -1;
460            int lteRsrq = -1;
461            int lteRssnr = SignalStrength.INVALID_SNR;
462            int lteCqi = -1;
463
464            int offset = 2;
465            int cdmaDbm = (ints[offset] > 0) ? -ints[offset] : -120;
466            int cdmaEcio = (ints[offset + 1] > 0) ? -ints[offset + 1] : -160;
467            int evdoRssi = (ints[offset + 2] > 0) ? -ints[offset + 2] : -120;
468            int evdoEcio = (ints[offset + 3] > 0) ? -ints[offset + 3] : -1;
469            int evdoSnr = ((ints[offset + 4] > 0) && (ints[offset + 4] <= 8)) ? ints[offset + 4]
470                    : -1;
471
472            if (networkType == ServiceState.RADIO_TECHNOLOGY_LTE) {
473                lteRssi = ints[offset+5];
474                lteRsrp = ints[offset+6];
475                lteRsrq = ints[offset+7];
476                lteRssnr = ints[offset+8];
477                lteCqi = ints[offset+9];
478            }
479
480            if (networkType != ServiceState.RADIO_TECHNOLOGY_LTE) {
481                mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio, evdoRssi, evdoEcio,
482                        evdoSnr, false);
483            } else {
484                mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio, evdoRssi, evdoEcio,
485                        evdoSnr, lteRssi, lteRsrp, lteRsrq, lteRssnr, lteCqi, true);
486            }
487        }
488
489        try {
490            phone.notifySignalStrength();
491        } catch (NullPointerException ex) {
492            loge("onSignalStrengthResult() Phone already destroyed: " + ex
493                    + "SignalStrength not notified");
494        }
495    }
496
497    @Override
498    public boolean isConcurrentVoiceAndDataAllowed() {
499        // Note: it needs to be confirmed which CDMA network types
500        // can support voice and data calls concurrently.
501        // For the time-being, the return value will be false.
502        return (networkType == ServiceState.RADIO_TECHNOLOGY_LTE);
503    }
504
505    /**
506     * Check whether the specified SID and NID pair appears in the HOME SID/NID list
507     * read from NV or SIM.
508     *
509     * @return true if provided sid/nid pair belongs to operator's home network.
510     */
511    private boolean isInHomeSidNid(int sid, int nid) {
512        // if SID/NID is not available, assume this is home network.
513        if (isSidsAllZeros()) return true;
514
515        // length of SID/NID shold be same
516        if (mHomeSystemId.length != mHomeNetworkId.length) return true;
517
518        if (sid == 0) return true;
519
520        for (int i = 0; i < mHomeSystemId.length; i++) {
521            // Use SID only if NID is a reserved value.
522            // SID 0 and NID 0 and 65535 are reserved. (C.0005 2.6.5.2)
523            if ((mHomeSystemId[i] == sid) &&
524                ((mHomeNetworkId[i] == 0) || (mHomeNetworkId[i] == 65535) ||
525                 (nid == 0) || (nid == 65535) || (mHomeNetworkId[i] == nid))) {
526                return true;
527            }
528        }
529        // SID/NID are not in the list. So device is not in home network
530        return false;
531    }
532
533    @Override
534    protected void log(String s) {
535        Log.d(LOG_TAG, "[CdmaLteSST] " + s);
536    }
537
538    @Override
539    protected void loge(String s) {
540        Log.e(LOG_TAG, "[CdmaLteSST] " + s);
541    }
542}
543