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