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