9f7947e0c6e2eb9ad26e268c858e4de7d10044ab
[android/platform/frameworks/opt/telephony.git] / src / java / com / android / internal / telephony / cdma / CdmaServiceStateTracker.java
1 /*
2  * Copyright (C) 2012 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
17 package com.android.internal.telephony.cdma;
18
19 import android.app.AlarmManager;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.database.ContentObserver;
24 import android.os.AsyncResult;
25 import android.os.Build;
26 import android.os.Handler;
27 import android.os.Message;
28 import android.os.PersistableBundle;
29 import android.os.PowerManager;
30 import android.os.Registrant;
31 import android.os.RegistrantList;
32 import android.os.RemoteException;
33 import android.os.ServiceManager;
34 import android.os.SystemClock;
35 import android.os.SystemProperties;
36 import android.os.UserHandle;
37 import android.provider.Settings;
38 import android.provider.Settings.SettingNotFoundException;
39 import android.telephony.CellInfo;
40 import android.telephony.CellInfoCdma;
41 import android.telephony.Rlog;
42 import android.telephony.ServiceState;
43 import android.telephony.SignalStrength;
44 import android.telephony.TelephonyManager;
45 import android.telephony.SubscriptionManager;
46 import android.telephony.cdma.CdmaCellLocation;
47 import android.text.TextUtils;
48 import android.util.EventLog;
49 import android.util.TimeUtils;
50
51 import com.android.internal.telephony.CommandException;
52 import com.android.internal.telephony.CommandsInterface;
53 import com.android.internal.telephony.CommandsInterface.RadioState;
54 import com.android.internal.telephony.EventLogTags;
55 import com.android.internal.telephony.ICarrierConfigLoader;
56 import com.android.internal.telephony.MccTable;
57 import com.android.internal.telephony.Phone;
58 import com.android.internal.telephony.PhoneConstants;
59 import com.android.internal.telephony.PhoneFactory;
60 import com.android.internal.telephony.ServiceStateTracker;
61 import com.android.internal.telephony.TelephonyIntents;
62 import com.android.internal.telephony.TelephonyProperties;
63 import com.android.internal.telephony.dataconnection.DcTrackerBase;
64 import com.android.internal.telephony.uicc.UiccCardApplication;
65 import com.android.internal.telephony.uicc.UiccController;
66 import com.android.internal.telephony.HbpcdUtils;
67
68 import java.io.FileDescriptor;
69 import java.io.PrintWriter;
70 import java.util.Arrays;
71 import java.util.Calendar;
72 import java.util.Date;
73 import java.util.List;
74 import java.util.TimeZone;
75
76 /**
77  * {@hide}
78  */
79 public class CdmaServiceStateTracker extends ServiceStateTracker {
80     static final String LOG_TAG = "CdmaSST";
81
82     CDMAPhone mPhone;
83     CdmaCellLocation mCellLoc;
84     CdmaCellLocation mNewCellLoc;
85
86     // Min values used to by getOtasp()
87     private static final String UNACTIVATED_MIN2_VALUE = "000000";
88     private static final String UNACTIVATED_MIN_VALUE = "1111110111";
89
90     private static final int MS_PER_HOUR = 60 * 60 * 1000;
91
92     // Current Otasp value
93     int mCurrentOtaspMode = OTASP_UNINITIALIZED;
94
95      /** if time between NITZ updates is less than mNitzUpdateSpacing the update may be ignored. */
96     private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10;
97     private int mNitzUpdateSpacing = SystemProperties.getInt("ro.nitz_update_spacing",
98             NITZ_UPDATE_SPACING_DEFAULT);
99
100     /** If mNitzUpdateSpacing hasn't been exceeded but update is > mNitzUpdate do the update */
101     private static final int NITZ_UPDATE_DIFF_DEFAULT = 2000;
102     private int mNitzUpdateDiff = SystemProperties.getInt("ro.nitz_update_diff",
103             NITZ_UPDATE_DIFF_DEFAULT);
104
105     private int mRoamingIndicator;
106     private boolean mIsInPrl;
107     private int mDefaultRoamingIndicator;
108
109     /**
110      * Initially assume no data connection.
111      */
112     protected int mRegistrationState = -1;
113     protected RegistrantList mCdmaForSubscriptionInfoReadyRegistrants = new RegistrantList();
114
115     /**
116      * Sometimes we get the NITZ time before we know what country we
117      * are in. Keep the time zone information from the NITZ string so
118      * we can fix the time zone once know the country.
119      */
120     protected boolean mNeedFixZone = false;
121     private int mZoneOffset;
122     private boolean mZoneDst;
123     private long mZoneTime;
124     protected boolean mGotCountryCode = false;
125     String mSavedTimeZone;
126     long mSavedTime;
127     long mSavedAtTime;
128
129     /** Wake lock used while setting time of day. */
130     private PowerManager.WakeLock mWakeLock;
131     private static final String WAKELOCK_TAG = "ServiceStateTracker";
132
133     protected String mMdn;
134     protected int mHomeSystemId[] = null;
135     protected int mHomeNetworkId[] = null;
136     protected String mMin;
137     protected String mPrlVersion;
138     protected boolean mIsMinInfoReady = false;
139
140     private boolean mIsEriTextLoaded = false;
141     protected boolean mIsSubscriptionFromRuim = false;
142     private CdmaSubscriptionSourceManager mCdmaSSM;
143
144     protected static final String INVALID_MCC = "000";
145     protected static final String DEFAULT_MNC = "00";
146
147     protected HbpcdUtils mHbpcdUtils = null;
148
149     /* Used only for debugging purposes. */
150     private String mRegistrationDeniedReason;
151
152     private ContentResolver mCr;
153     private String mCurrentCarrier = null;
154
155     private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
156         @Override
157         public void onChange(boolean selfChange) {
158             if (DBG) log("Auto time state changed");
159             revertToNitzTime();
160         }
161     };
162
163     private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) {
164         @Override
165         public void onChange(boolean selfChange) {
166             if (DBG) log("Auto time zone state changed");
167             revertToNitzTimeZone();
168         }
169     };
170
171     public CdmaServiceStateTracker(CDMAPhone phone) {
172         this(phone, new CellInfoCdma());
173     }
174
175     protected CdmaServiceStateTracker(CDMAPhone phone, CellInfo cellInfo) {
176         super(phone, phone.mCi, cellInfo);
177
178         mPhone = phone;
179         mCr = phone.getContext().getContentResolver();
180         mCellLoc = new CdmaCellLocation();
181         mNewCellLoc = new CdmaCellLocation();
182
183         mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(phone.getContext(), mCi, this,
184                 EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
185         mIsSubscriptionFromRuim = (mCdmaSSM.getCdmaSubscriptionSource() ==
186                           CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM);
187
188         PowerManager powerManager =
189                 (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE);
190         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
191
192         mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
193
194         mCi.registerForVoiceNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED_CDMA, null);
195         mCi.setOnNITZTime(this, EVENT_NITZ_TIME, null);
196
197         mCi.registerForCdmaPrlChanged(this, EVENT_CDMA_PRL_VERSION_CHANGED, null);
198         phone.registerForEriFileLoaded(this, EVENT_ERI_FILE_LOADED, null);
199         mCi.registerForCdmaOtaProvision(this,EVENT_OTA_PROVISION_STATUS_CHANGE, null);
200
201         // System setting property AIRPLANE_MODE_ON is set in Settings.
202         int airplaneMode = Settings.Global.getInt(mCr, Settings.Global.AIRPLANE_MODE_ON, 0);
203         mDesiredPowerState = ! (airplaneMode > 0);
204
205         mCr.registerContentObserver(
206                 Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
207                 mAutoTimeObserver);
208         mCr.registerContentObserver(
209             Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
210             mAutoTimeZoneObserver);
211         setSignalStrengthDefaultValues();
212
213         mHbpcdUtils = new HbpcdUtils(phone.getContext());
214
215         // Reset OTASP state in case previously set by another service
216         phone.notifyOtaspChanged(OTASP_UNINITIALIZED);
217     }
218
219     @Override
220     public void dispose() {
221         checkCorrectThread();
222         log("ServiceStateTracker dispose");
223
224         // Unregister for all events.
225         mCi.unregisterForRadioStateChanged(this);
226         mCi.unregisterForVoiceNetworkStateChanged(this);
227         mCi.unregisterForCdmaOtaProvision(this);
228         mPhone.unregisterForEriFileLoaded(this);
229         if (mUiccApplcation != null) {mUiccApplcation.unregisterForReady(this);}
230         if (mIccRecords != null) {mIccRecords.unregisterForRecordsLoaded(this);}
231         mCi.unSetOnNITZTime(this);
232         mCr.unregisterContentObserver(mAutoTimeObserver);
233         mCr.unregisterContentObserver(mAutoTimeZoneObserver);
234         mCdmaSSM.dispose(this);
235         mCi.unregisterForCdmaPrlChanged(this);
236         super.dispose();
237     }
238
239     @Override
240     protected void finalize() {
241         if (DBG) log("CdmaServiceStateTracker finalized");
242     }
243
244     /**
245      * Registration point for subscription info ready
246      * @param h handler to notify
247      * @param what what code of message when delivered
248      * @param obj placed in Message.obj
249      */
250     public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
251         Registrant r = new Registrant(h, what, obj);
252         mCdmaForSubscriptionInfoReadyRegistrants.add(r);
253
254         if (isMinInfoReady()) {
255             r.notifyRegistrant();
256         }
257     }
258
259     public void unregisterForSubscriptionInfoReady(Handler h) {
260         mCdmaForSubscriptionInfoReadyRegistrants.remove(h);
261     }
262
263     /**
264      * Save current source of cdma subscription
265      * @param source - 1 for NV, 0 for RUIM
266      */
267     private void saveCdmaSubscriptionSource(int source) {
268         log("Storing cdma subscription source: " + source);
269         Settings.Global.putInt(mPhone.getContext().getContentResolver(),
270                 Settings.Global.CDMA_SUBSCRIPTION_MODE,
271                 source );
272         log("Read from settings: " + Settings.Global.getInt(mPhone.getContext().getContentResolver(),
273                     Settings.Global.CDMA_SUBSCRIPTION_MODE, -1));
274     }
275
276     private void getSubscriptionInfoAndStartPollingThreads() {
277         mCi.getCDMASubscription(obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
278
279         // Get Registration Information
280         pollState();
281     }
282
283     @Override
284     public void handleMessage (Message msg) {
285         AsyncResult ar;
286         int[] ints;
287         String[] strings;
288
289         if (!mPhone.mIsTheCurrentActivePhone) {
290             loge("Received message " + msg + "[" + msg.what + "]" +
291                     " while being destroyed. Ignoring.");
292             return;
293         }
294
295         switch (msg.what) {
296         case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
297             handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource());
298             break;
299
300         case EVENT_RUIM_READY:
301             if (mPhone.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE) {
302                 // Subscription will be read from SIM I/O
303                 if (DBG) log("Receive EVENT_RUIM_READY");
304                 pollState();
305             } else {
306                 if (DBG) log("Receive EVENT_RUIM_READY and Send Request getCDMASubscription.");
307                 getSubscriptionInfoAndStartPollingThreads();
308             }
309
310             // Only support automatic selection mode in CDMA.
311             mCi.getNetworkSelectionMode(obtainMessage(EVENT_POLL_STATE_NETWORK_SELECTION_MODE));
312
313             mPhone.prepareEri();
314             break;
315
316         case EVENT_NV_READY:
317             updatePhoneObject();
318
319             // Only support automatic selection mode in CDMA.
320             mCi.getNetworkSelectionMode(obtainMessage(EVENT_POLL_STATE_NETWORK_SELECTION_MODE));
321
322             // For Non-RUIM phones, the subscription information is stored in
323             // Non Volatile. Here when Non-Volatile is ready, we can poll the CDMA
324             // subscription info.
325             getSubscriptionInfoAndStartPollingThreads();
326             break;
327
328         case EVENT_RADIO_STATE_CHANGED:
329             if(mCi.getRadioState() == RadioState.RADIO_ON) {
330                 handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource());
331
332                 // Signal strength polling stops when radio is off.
333                 queueNextSignalStrengthPoll();
334             }
335             // This will do nothing in the 'radio not available' case.
336             setPowerStateToDesired();
337             pollState();
338             break;
339
340         case EVENT_NETWORK_STATE_CHANGED_CDMA:
341             pollState();
342             break;
343
344         case EVENT_GET_SIGNAL_STRENGTH:
345             // This callback is called when signal strength is polled
346             // all by itself.
347
348             if (!(mCi.getRadioState().isOn())) {
349                 // Polling will continue when radio turns back on.
350                 return;
351             }
352             ar = (AsyncResult) msg.obj;
353             onSignalStrengthResult(ar, false);
354             queueNextSignalStrengthPoll();
355
356             break;
357
358         case EVENT_GET_LOC_DONE_CDMA:
359             ar = (AsyncResult) msg.obj;
360
361             if (ar.exception == null) {
362                 String states[] = (String[])ar.result;
363                 int baseStationId = -1;
364                 int baseStationLatitude = CdmaCellLocation.INVALID_LAT_LONG;
365                 int baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
366                 int systemId = -1;
367                 int networkId = -1;
368
369                 if (states.length > 9) {
370                     try {
371                         if (states[4] != null) {
372                             baseStationId = Integer.parseInt(states[4]);
373                         }
374                         if (states[5] != null) {
375                             baseStationLatitude = Integer.parseInt(states[5]);
376                         }
377                         if (states[6] != null) {
378                             baseStationLongitude = Integer.parseInt(states[6]);
379                         }
380                         // Some carriers only return lat-lngs of 0,0
381                         if (baseStationLatitude == 0 && baseStationLongitude == 0) {
382                             baseStationLatitude  = CdmaCellLocation.INVALID_LAT_LONG;
383                             baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
384                         }
385                         if (states[8] != null) {
386                             systemId = Integer.parseInt(states[8]);
387                         }
388                         if (states[9] != null) {
389                             networkId = Integer.parseInt(states[9]);
390                         }
391                     } catch (NumberFormatException ex) {
392                         loge("error parsing cell location data: " + ex);
393                     }
394                 }
395
396                 mCellLoc.setCellLocationData(baseStationId, baseStationLatitude,
397                         baseStationLongitude, systemId, networkId);
398                 mPhone.notifyLocationChanged();
399             }
400
401             // Release any temporary cell lock, which could have been
402             // acquired to allow a single-shot location update.
403             disableSingleLocationUpdate();
404             break;
405
406         case EVENT_POLL_STATE_REGISTRATION_CDMA:
407         case EVENT_POLL_STATE_OPERATOR_CDMA:
408         case EVENT_POLL_STATE_GPRS:
409             ar = (AsyncResult) msg.obj;
410             handlePollStateResult(msg.what, ar);
411             break;
412
413         case EVENT_POLL_STATE_CDMA_SUBSCRIPTION: // Handle RIL_CDMA_SUBSCRIPTION
414             ar = (AsyncResult) msg.obj;
415
416             if (ar.exception == null) {
417                 String cdmaSubscription[] = (String[])ar.result;
418                 if (cdmaSubscription != null && cdmaSubscription.length >= 5) {
419                     mMdn = cdmaSubscription[0];
420                     parseSidNid(cdmaSubscription[1], cdmaSubscription[2]);
421
422                     mMin = cdmaSubscription[3];
423                     mPrlVersion = cdmaSubscription[4];
424                     if (DBG) log("GET_CDMA_SUBSCRIPTION: MDN=" + mMdn);
425
426                     mIsMinInfoReady = true;
427
428                     updateOtaspState();
429                     if (!mIsSubscriptionFromRuim && mIccRecords != null) {
430                         if (DBG) {
431                             log("GET_CDMA_SUBSCRIPTION set imsi in mIccRecords");
432                         }
433                         mIccRecords.setImsi(getImsi());
434                     } else {
435                         if (DBG) {
436                             log("GET_CDMA_SUBSCRIPTION either mIccRecords is null  or NV type device" +
437                                     " - not setting Imsi in mIccRecords");
438                         }
439                     }
440                 } else {
441                     if (DBG) {
442                         log("GET_CDMA_SUBSCRIPTION: error parsing cdmaSubscription params num="
443                             + cdmaSubscription.length);
444                     }
445                 }
446             }
447             break;
448
449         case EVENT_POLL_SIGNAL_STRENGTH:
450             // Just poll signal strength...not part of pollState()
451
452             mCi.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH));
453             break;
454
455         case EVENT_NITZ_TIME:
456             ar = (AsyncResult) msg.obj;
457
458             String nitzString = (String)((Object[])ar.result)[0];
459             long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue();
460
461             setTimeFromNITZString(nitzString, nitzReceiveTime);
462             break;
463
464         case EVENT_SIGNAL_STRENGTH_UPDATE:
465             // This is a notification from CommandsInterface.setOnSignalStrengthUpdate.
466
467             ar = (AsyncResult) msg.obj;
468
469             // The radio is telling us about signal strength changes,
470             // so we don't have to ask it.
471             mDontPollSignalStrength = true;
472
473             onSignalStrengthResult(ar, false);
474             break;
475
476         case EVENT_RUIM_RECORDS_LOADED:
477             log("EVENT_RUIM_RECORDS_LOADED: what=" + msg.what);
478             updatePhoneObject();
479             updateSpnDisplay();
480             break;
481
482         case EVENT_LOCATION_UPDATES_ENABLED:
483             ar = (AsyncResult) msg.obj;
484
485             if (ar.exception == null) {
486                 mCi.getVoiceRegistrationState(obtainMessage(EVENT_GET_LOC_DONE_CDMA, null));
487             }
488             break;
489
490         case EVENT_ERI_FILE_LOADED:
491             // Repoll the state once the ERI file has been loaded.
492             if (DBG) log("[CdmaServiceStateTracker] ERI file has been loaded, repolling.");
493             pollState();
494             break;
495
496         case EVENT_OTA_PROVISION_STATUS_CHANGE:
497             ar = (AsyncResult)msg.obj;
498             if (ar.exception == null) {
499                 ints = (int[]) ar.result;
500                 int otaStatus = ints[0];
501                 if (otaStatus == Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED
502                     || otaStatus == Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED) {
503                     if (DBG) log("EVENT_OTA_PROVISION_STATUS_CHANGE: Complete, Reload MDN");
504                     mCi.getCDMASubscription( obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
505                 }
506             }
507             break;
508
509         case EVENT_CDMA_PRL_VERSION_CHANGED:
510             ar = (AsyncResult)msg.obj;
511             if (ar.exception == null) {
512                 ints = (int[]) ar.result;
513                 mPrlVersion = Integer.toString(ints[0]);
514             }
515             break;
516
517         case EVENT_CHANGE_IMS_STATE:
518             if (DBG) log("EVENT_CHANGE_IMS_STATE");
519             setPowerStateToDesired();
520             break;
521
522         case EVENT_POLL_STATE_NETWORK_SELECTION_MODE:
523             if (DBG) log("EVENT_POLL_STATE_NETWORK_SELECTION_MODE");
524             ar = (AsyncResult) msg.obj;
525             if (ar.exception == null && ar.result != null) {
526                 ints = (int[])ar.result;
527                 if (ints[0] == 1) {  // Manual selection.
528                     mPhone.setNetworkSelectionModeAutomatic(null);
529                 }
530             } else {
531                 log("Unable to getNetworkSelectionMode");
532             }
533             break;
534
535         default:
536             super.handleMessage(msg);
537         break;
538         }
539     }
540
541     private void handleCdmaSubscriptionSource(int newSubscriptionSource) {
542         log("Subscription Source : " + newSubscriptionSource);
543         mIsSubscriptionFromRuim =
544             (newSubscriptionSource == CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM);
545         log("isFromRuim: " + mIsSubscriptionFromRuim);
546         saveCdmaSubscriptionSource(newSubscriptionSource);
547         if (!mIsSubscriptionFromRuim) {
548             // NV is ready when subscription source is NV
549             sendMessage(obtainMessage(EVENT_NV_READY));
550         }
551     }
552
553     @Override
554     protected void setPowerStateToDesired() {
555         // If we want it on and it's off, turn it on
556         if (mDesiredPowerState
557             && mCi.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
558             mCi.setRadioPower(true, null);
559         } else if (!mDesiredPowerState && mCi.getRadioState().isOn()) {
560             DcTrackerBase dcTracker = mPhone.mDcTracker;
561
562             // If it's on and available and we want it off gracefully
563             powerOffRadioSafely(dcTracker);
564         } else if (mDeviceShuttingDown && mCi.getRadioState().isAvailable()) {
565             mCi.requestShutdown(null);
566         }
567     }
568
569     @Override
570     protected void updateSpnDisplay() {
571         // mOperatorAlphaLong contains the ERI text
572         String plmn = mSS.getOperatorAlphaLong();
573         boolean showPlmn = false;
574
575         if (!TextUtils.equals(plmn, mCurPlmn)) {
576             // Allow A blank plmn, "" to set showPlmn to true. Previously, we
577             // would set showPlmn to true only if plmn was not empty, i.e. was not
578             // null and not blank. But this would cause us to incorrectly display
579             // "No Service". Now showPlmn is set to true for any non null string.
580             showPlmn = plmn != null;
581             if (DBG) {
582                 log(String.format("updateSpnDisplay: changed sending intent" +
583                             " showPlmn='%b' plmn='%s'", showPlmn, plmn));
584             }
585             Intent intent = new Intent(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
586             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
587             intent.putExtra(TelephonyIntents.EXTRA_SHOW_SPN, false);
588             intent.putExtra(TelephonyIntents.EXTRA_SPN, "");
589             intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
590             intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
591             SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
592             mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
593
594             if (!mSubscriptionController.setPlmnSpn(mPhone.getPhoneId(),
595                     showPlmn, plmn, false, "")) {
596                 mSpnUpdatePending = true;
597             }
598         }
599
600         mCurShowSpn = false;
601         mCurShowPlmn = showPlmn;
602         mCurSpn = "";
603         mCurPlmn = plmn;
604     }
605
606     @Override
607     protected Phone getPhone() {
608         return mPhone;
609     }
610
611     /**
612     * Hanlde the PollStateResult message
613     */
614     protected void handlePollStateResultMessage(int what, AsyncResult ar){
615         int ints[];
616         String states[];
617         switch (what) {
618             case EVENT_POLL_STATE_GPRS: {
619                 states = (String[])ar.result;
620                 if (DBG) {
621                     log("handlePollStateResultMessage: EVENT_POLL_STATE_GPRS states.length=" +
622                             states.length + " states=" + states);
623                 }
624
625                 int regState = ServiceState.RIL_REG_STATE_UNKNOWN;
626                 int dataRadioTechnology = 0;
627
628                 if (states.length > 0) {
629                     try {
630                         regState = Integer.parseInt(states[0]);
631     
632                         // states[3] (if present) is the current radio technology
633                         if (states.length >= 4 && states[3] != null) {
634                             dataRadioTechnology = Integer.parseInt(states[3]);
635                         }
636                     } catch (NumberFormatException ex) {
637                         loge("handlePollStateResultMessage: error parsing GprsRegistrationState: "
638                                         + ex);
639                     }
640                 }
641
642                 int dataRegState = regCodeToServiceState(regState);
643                 mNewSS.setDataRegState(dataRegState);
644                 mNewSS.setRilDataRadioTechnology(dataRadioTechnology);
645                 mNewSS.setDataRoaming(regCodeIsRoaming(regState));
646                 if (DBG) {
647                     log("handlPollStateResultMessage: cdma setDataRegState=" + dataRegState
648                             + " regState=" + regState
649                             + " dataRadioTechnology=" + dataRadioTechnology);
650                 }
651                 break;
652             }
653
654             case EVENT_POLL_STATE_REGISTRATION_CDMA: // Handle RIL_REQUEST_REGISTRATION_STATE.
655                 states = (String[])ar.result;
656
657                 int registrationState = 4;     //[0] registrationState
658                 int radioTechnology = -1;      //[3] radioTechnology
659                 int baseStationId = -1;        //[4] baseStationId
660                 //[5] baseStationLatitude
661                 int baseStationLatitude = CdmaCellLocation.INVALID_LAT_LONG;
662                 //[6] baseStationLongitude
663                 int baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
664                 int cssIndicator = 0;          //[7] init with 0, because it is treated as a boolean
665                 int systemId = 0;              //[8] systemId
666                 int networkId = 0;             //[9] networkId
667                 int roamingIndicator = -1;     //[10] Roaming indicator
668                 int systemIsInPrl = 0;         //[11] Indicates if current system is in PRL
669                 int defaultRoamingIndicator = 0;  //[12] Is default roaming indicator from PRL
670                 int reasonForDenial = 0;       //[13] Denial reason if registrationState = 3
671
672                 if (states.length >= 14) {
673                     try {
674                         if (states[0] != null) {
675                             registrationState = Integer.parseInt(states[0]);
676                         }
677                         if (states[3] != null) {
678                             radioTechnology = Integer.parseInt(states[3]);
679                         }
680                         if (states[4] != null) {
681                             baseStationId = Integer.parseInt(states[4]);
682                         }
683                         if (states[5] != null) {
684                             baseStationLatitude = Integer.parseInt(states[5]);
685                         }
686                         if (states[6] != null) {
687                             baseStationLongitude = Integer.parseInt(states[6]);
688                         }
689                         // Some carriers only return lat-lngs of 0,0
690                         if (baseStationLatitude == 0 && baseStationLongitude == 0) {
691                             baseStationLatitude  = CdmaCellLocation.INVALID_LAT_LONG;
692                             baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
693                         }
694                         if (states[7] != null) {
695                             cssIndicator = Integer.parseInt(states[7]);
696                         }
697                         if (states[8] != null) {
698                             systemId = Integer.parseInt(states[8]);
699                         }
700                         if (states[9] != null) {
701                             networkId = Integer.parseInt(states[9]);
702                         }
703                         if (states[10] != null) {
704                             roamingIndicator = Integer.parseInt(states[10]);
705                         }
706                         if (states[11] != null) {
707                             systemIsInPrl = Integer.parseInt(states[11]);
708                         }
709                         if (states[12] != null) {
710                             defaultRoamingIndicator = Integer.parseInt(states[12]);
711                         }
712                         if (states[13] != null) {
713                             reasonForDenial = Integer.parseInt(states[13]);
714                         }
715                     } catch (NumberFormatException ex) {
716                         loge("EVENT_POLL_STATE_REGISTRATION_CDMA: error parsing: " + ex);
717                     }
718                 } else {
719                     throw new RuntimeException("Warning! Wrong number of parameters returned from "
720                                          + "RIL_REQUEST_REGISTRATION_STATE: expected 14 or more "
721                                          + "strings and got " + states.length + " strings");
722                 }
723
724                 mRegistrationState = registrationState;
725                 // When registration state is roaming and TSB58
726                 // roaming indicator is not in the carrier-specified
727                 // list of ERIs for home system, mCdmaRoaming is true.
728                 boolean cdmaRoaming =
729                         regCodeIsRoaming(registrationState) && !isRoamIndForHomeSystem(states[10]);
730                 mNewSS.setVoiceRoaming(cdmaRoaming);
731                 mNewSS.setState (regCodeToServiceState(registrationState));
732
733                 mNewSS.setRilVoiceRadioTechnology(radioTechnology);
734
735                 mNewSS.setCssIndicator(cssIndicator);
736                 mNewSS.setSystemAndNetworkId(systemId, networkId);
737                 mRoamingIndicator = roamingIndicator;
738                 mIsInPrl = (systemIsInPrl == 0) ? false : true;
739                 mDefaultRoamingIndicator = defaultRoamingIndicator;
740
741
742                 // Values are -1 if not available.
743                 mNewCellLoc.setCellLocationData(baseStationId, baseStationLatitude,
744                         baseStationLongitude, systemId, networkId);
745
746                 if (reasonForDenial == 0) {
747                     mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_GEN;
748                 } else if (reasonForDenial == 1) {
749                     mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_AUTH;
750                 } else {
751                     mRegistrationDeniedReason = "";
752                 }
753
754                 if (mRegistrationState == 3) {
755                     if (DBG) log("Registration denied, " + mRegistrationDeniedReason);
756                 }
757                 break;
758
759             case EVENT_POLL_STATE_OPERATOR_CDMA: // Handle RIL_REQUEST_OPERATOR
760                 String opNames[] = (String[])ar.result;
761
762                 if (opNames != null && opNames.length >= 3) {
763                     // TODO: Do we care about overriding in this case.
764                     // If the NUMERIC field isn't valid use PROPERTY_CDMA_HOME_OPERATOR_NUMERIC
765                     if ((opNames[2] == null) || (opNames[2].length() < 5)
766                             || ("00000".equals(opNames[2]))) {
767                         opNames[2] = SystemProperties.get(
768                                 CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC, "00000");
769                         if (DBG) {
770                             log("RIL_REQUEST_OPERATOR.response[2], the numeric, " +
771                                     " is bad. Using SystemProperties '" +
772                                             CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC +
773                                     "'= " + opNames[2]);
774                         }
775                     }
776
777                     if (!mIsSubscriptionFromRuim) {
778                         // NV device (as opposed to CSIM)
779                         mNewSS.setOperatorName(opNames[0], opNames[1], opNames[2]);
780                     } else {
781                         String brandOverride = mUiccController.getUiccCard(getPhoneId()) != null ?
782                             mUiccController.getUiccCard(getPhoneId()).getOperatorBrandOverride() : null;
783                         if (brandOverride != null) {
784                             mNewSS.setOperatorName(brandOverride, brandOverride, opNames[2]);
785                         } else {
786                             mNewSS.setOperatorName(opNames[0], opNames[1], opNames[2]);
787                         }
788                     }
789                 } else {
790                     if (DBG) log("EVENT_POLL_STATE_OPERATOR_CDMA: error parsing opNames");
791                 }
792                 break;
793
794             default:
795                 loge("handlePollStateResultMessage: RIL response handle in wrong phone!"
796                         + " Expected CDMA RIL request and get GSM RIL request.");
797                 break;
798         }
799     }
800
801     /**
802      * Handle the result of one of the pollState() - related requests
803      */
804     @Override
805     protected void handlePollStateResult(int what, AsyncResult ar) {
806         // Ignore stale requests from last poll.
807         if (ar.userObj != mPollingContext) return;
808
809         if (ar.exception != null) {
810             CommandException.Error err=null;
811
812             if (ar.exception instanceof CommandException) {
813                 err = ((CommandException)(ar.exception)).getCommandError();
814             }
815
816             if (err == CommandException.Error.RADIO_NOT_AVAILABLE) {
817                 // Radio has crashed or turned off.
818                 cancelPollState();
819                 return;
820             }
821
822             if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
823                 loge("handlePollStateResult: RIL returned an error where it must succeed"
824                         + ar.exception);
825             }
826         } else try {
827             handlePollStateResultMessage(what, ar);
828         } catch (RuntimeException ex) {
829             loge("handlePollStateResult: Exception while polling service state. "
830                     + "Probably malformed RIL response." + ex);
831         }
832
833         mPollingContext[0]--;
834
835         if (mPollingContext[0] == 0) {
836             boolean namMatch = false;
837             if (!isSidsAllZeros() && isHomeSid(mNewSS.getSystemId())) {
838                 namMatch = true;
839             }
840
841             // Setting SS Roaming (general)
842             if (mIsSubscriptionFromRuim) {
843                 mNewSS.setVoiceRoaming(isRoamingBetweenOperators(mNewSS.getVoiceRoaming(), mNewSS));
844             }
845             // For CDMA, voice and data should have the same roaming status
846             final boolean isVoiceInService =
847                     (mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE);
848             final int dataRegType = mNewSS.getRilDataRadioTechnology();
849             if (isVoiceInService && ServiceState.isCdma(dataRegType)) {
850                 mNewSS.setDataRoaming(mNewSS.getVoiceRoaming());
851             }
852
853             // Setting SS CdmaRoamingIndicator and CdmaDefaultRoamingIndicator
854             mNewSS.setCdmaDefaultRoamingIndicator(mDefaultRoamingIndicator);
855             mNewSS.setCdmaRoamingIndicator(mRoamingIndicator);
856             boolean isPrlLoaded = true;
857             if (TextUtils.isEmpty(mPrlVersion)) {
858                 isPrlLoaded = false;
859             }
860             if (!isPrlLoaded || (mNewSS.getRilVoiceRadioTechnology()
861                                         == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)) {
862                 log("Turn off roaming indicator if !isPrlLoaded or voice RAT is unknown");
863                 mNewSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF);
864             } else if (!isSidsAllZeros()) {
865                 if (!namMatch && !mIsInPrl) {
866                     // Use default
867                     mNewSS.setCdmaRoamingIndicator(mDefaultRoamingIndicator);
868                 } else if (namMatch && !mIsInPrl) {
869                     // TODO this will be removed when we handle roaming on LTE on CDMA+LTE phones
870                     if (mNewSS.getRilVoiceRadioTechnology()
871                             == ServiceState.RIL_RADIO_TECHNOLOGY_LTE) {
872                         log("Turn off roaming indicator as voice is LTE");
873                         mNewSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF);
874                     } else {
875                         mNewSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_FLASH);
876                     }
877                 } else if (!namMatch && mIsInPrl) {
878                     // Use the one from PRL/ERI
879                     mNewSS.setCdmaRoamingIndicator(mRoamingIndicator);
880                 } else {
881                     // It means namMatch && mIsInPrl
882                     if ((mRoamingIndicator <= 2)) {
883                         mNewSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF);
884                     } else {
885                         // Use the one from PRL/ERI
886                         mNewSS.setCdmaRoamingIndicator(mRoamingIndicator);
887                     }
888                 }
889             }
890
891             int roamingIndicator = mNewSS.getCdmaRoamingIndicator();
892             mNewSS.setCdmaEriIconIndex(mPhone.mEriManager.getCdmaEriIconIndex(roamingIndicator,
893                     mDefaultRoamingIndicator));
894             mNewSS.setCdmaEriIconMode(mPhone.mEriManager.getCdmaEriIconMode(roamingIndicator,
895                     mDefaultRoamingIndicator));
896
897             // NOTE: Some operator may require overriding mCdmaRoaming
898             // (set by the modem), depending on the mRoamingIndicator.
899
900             if (DBG) {
901                 log("Set CDMA Roaming Indicator to: " + mNewSS.getCdmaRoamingIndicator()
902                     + ". voiceRoaming = " + mNewSS.getVoiceRoaming()
903                     + ". dataRoaming = " + mNewSS.getDataRoaming()
904                     + ", isPrlLoaded = " + isPrlLoaded
905                     + ". namMatch = " + namMatch + " , mIsInPrl = " + mIsInPrl
906                     + ", mRoamingIndicator = " + mRoamingIndicator
907                     + ", mDefaultRoamingIndicator= " + mDefaultRoamingIndicator);
908             }
909             pollStateDone();
910         }
911
912     }
913
914     /**
915      * Set both voice and data roaming type,
916      * judging from the roaming indicator
917      * or ISO country of SIM VS network.
918      */
919     protected void setRoamingType(ServiceState currentServiceState) {
920         final boolean isVoiceInService =
921                 (currentServiceState.getVoiceRegState() == ServiceState.STATE_IN_SERVICE);
922         if (isVoiceInService) {
923             if (currentServiceState.getVoiceRoaming()) {
924                 // some carrier defines international roaming by indicator
925                 int[] intRoamingIndicators = mPhone.getContext().getResources().getIntArray(
926                         com.android.internal.R.array.config_cdma_international_roaming_indicators);
927                 if ((intRoamingIndicators != null) && (intRoamingIndicators.length > 0)) {
928                     // It's domestic roaming at least now
929                     currentServiceState.setVoiceRoamingType(ServiceState.ROAMING_TYPE_DOMESTIC);
930                     int curRoamingIndicator = currentServiceState.getCdmaRoamingIndicator();
931                     for (int i = 0; i < intRoamingIndicators.length; i++) {
932                         if (curRoamingIndicator == intRoamingIndicators[i]) {
933                             currentServiceState.setVoiceRoamingType(
934                                     ServiceState.ROAMING_TYPE_INTERNATIONAL);
935                             break;
936                         }
937                     }
938                 } else {
939                     // check roaming type by MCC
940                     if (inSameCountry(currentServiceState.getVoiceOperatorNumeric())) {
941                         currentServiceState.setVoiceRoamingType(
942                                 ServiceState.ROAMING_TYPE_DOMESTIC);
943                     } else {
944                         currentServiceState.setVoiceRoamingType(
945                                 ServiceState.ROAMING_TYPE_INTERNATIONAL);
946                     }
947                 }
948             } else {
949                 currentServiceState.setVoiceRoamingType(ServiceState.ROAMING_TYPE_NOT_ROAMING);
950             }
951         }
952         final boolean isDataInService =
953                 (currentServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE);
954         final int dataRegType = currentServiceState.getRilDataRadioTechnology();
955         if (isDataInService) {
956             if (!currentServiceState.getDataRoaming()) {
957                 currentServiceState.setDataRoamingType(ServiceState.ROAMING_TYPE_NOT_ROAMING);
958             } else if (ServiceState.isCdma(dataRegType)) {
959                 if (isVoiceInService) {
960                     // CDMA data should have the same state as voice
961                     currentServiceState.setDataRoamingType(currentServiceState
962                             .getVoiceRoamingType());
963                 } else {
964                     // we can not decide CDMA data roaming type without voice
965                     // set it as same as last time
966                     currentServiceState.setDataRoamingType(ServiceState.ROAMING_TYPE_UNKNOWN);
967                 }
968             } else {
969                 // take it as 3GPP roaming
970                 if (inSameCountry(currentServiceState.getDataOperatorNumeric())) {
971                     currentServiceState.setDataRoamingType(ServiceState.ROAMING_TYPE_DOMESTIC);
972                 } else {
973                     currentServiceState.setDataRoamingType(
974                             ServiceState.ROAMING_TYPE_INTERNATIONAL);
975                 }
976             }
977         }
978     }
979
980     protected String getHomeOperatorNumeric() {
981         String numeric = ((TelephonyManager) mPhone.getContext().
982                 getSystemService(Context.TELEPHONY_SERVICE)).
983                 getSimOperatorNumericForPhone(mPhoneBase.getPhoneId());
984         if (TextUtils.isEmpty(numeric)) {
985             numeric = SystemProperties.get(CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC, "");
986         }
987         return numeric;
988     }
989
990     protected void setSignalStrengthDefaultValues() {
991         mSignalStrength = new SignalStrength( false);
992     }
993
994     /**
995      * A complete "service state" from our perspective is
996      * composed of a handful of separate requests to the radio.
997      *
998      * We make all of these requests at once, but then abandon them
999      * and start over again if the radio notifies us that some
1000      * event has changed
1001      */
1002     @Override
1003     public void pollState() {
1004         mPollingContext = new int[1];
1005         mPollingContext[0] = 0;
1006
1007         switch (mCi.getRadioState()) {
1008         case RADIO_UNAVAILABLE:
1009             mNewSS.setStateOutOfService();
1010             mNewCellLoc.setStateInvalid();
1011             setSignalStrengthDefaultValues();
1012             mGotCountryCode = false;
1013
1014             pollStateDone();
1015             break;
1016
1017         case RADIO_OFF:
1018             mNewSS.setStateOff();
1019             mNewCellLoc.setStateInvalid();
1020             setSignalStrengthDefaultValues();
1021             mGotCountryCode = false;
1022
1023             if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
1024                         != mSS.getRilDataRadioTechnology()) {
1025                 pollStateDone();
1026             }
1027
1028         default:
1029             // Issue all poll-related commands at once, then count
1030             // down the responses which are allowed to arrive
1031             // out-of-order.
1032
1033             mPollingContext[0]++;
1034             // RIL_REQUEST_OPERATOR is necessary for CDMA
1035             mCi.getOperator(
1036                     obtainMessage(EVENT_POLL_STATE_OPERATOR_CDMA, mPollingContext));
1037
1038             mPollingContext[0]++;
1039             // RIL_REQUEST_VOICE_REGISTRATION_STATE is necessary for CDMA
1040             mCi.getVoiceRegistrationState(
1041                     obtainMessage(EVENT_POLL_STATE_REGISTRATION_CDMA, mPollingContext));
1042
1043             mPollingContext[0]++;
1044             // RIL_REQUEST_DATA_REGISTRATION_STATE
1045             mCi.getDataRegistrationState(obtainMessage(EVENT_POLL_STATE_GPRS,
1046                                         mPollingContext));
1047             break;
1048         }
1049     }
1050
1051     protected void fixTimeZone(String isoCountryCode) {
1052         TimeZone zone = null;
1053         // If the offset is (0, false) and the time zone property
1054         // is set, use the time zone property rather than GMT.
1055         String zoneName = SystemProperties.get(TIMEZONE_PROPERTY);
1056         if (DBG) {
1057             log("fixTimeZone zoneName='" + zoneName +
1058                 "' mZoneOffset=" + mZoneOffset + " mZoneDst=" + mZoneDst +
1059                 " iso-cc='" + isoCountryCode +
1060                 "' iso-cc-idx=" + Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode));
1061         }
1062         if ((mZoneOffset == 0) && (mZoneDst == false) && (zoneName != null)
1063                 && (zoneName.length() > 0)
1064                 && (Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode) < 0)) {
1065             // For NITZ string without time zone,
1066             // need adjust time to reflect default time zone setting
1067             zone = TimeZone.getDefault();
1068             if (mNeedFixZone) {
1069                 long ctm = System.currentTimeMillis();
1070                 long tzOffset = zone.getOffset(ctm);
1071                 if (DBG) {
1072                     log("fixTimeZone: tzOffset=" + tzOffset +
1073                             " ltod=" + TimeUtils.logTimeOfDay(ctm));
1074                 }
1075                 if (getAutoTime()) {
1076                     long adj = ctm - tzOffset;
1077                     if (DBG) log("fixTimeZone: adj ltod=" + TimeUtils.logTimeOfDay(adj));
1078                     setAndBroadcastNetworkSetTime(adj);
1079                 } else {
1080                     // Adjust the saved NITZ time to account for tzOffset.
1081                     mSavedTime = mSavedTime - tzOffset;
1082                     if (DBG) log("fixTimeZone: adj mSavedTime=" + mSavedTime);
1083                 }
1084             }
1085             if (DBG) log("fixTimeZone: using default TimeZone");
1086         } else if (isoCountryCode.equals("")) {
1087             // Country code not found. This is likely a test network.
1088             // Get a TimeZone based only on the NITZ parameters (best guess).
1089             zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime);
1090             if (DBG) log("fixTimeZone: using NITZ TimeZone");
1091         } else {
1092             zone = TimeUtils.getTimeZone(mZoneOffset, mZoneDst, mZoneTime, isoCountryCode);
1093             if (DBG) log("fixTimeZone: using getTimeZone(off, dst, time, iso)");
1094         }
1095
1096         mNeedFixZone = false;
1097
1098         if (zone != null) {
1099             log("fixTimeZone: zone != null zone.getID=" + zone.getID());
1100             if (getAutoTimeZone()) {
1101                 setAndBroadcastNetworkSetTimeZone(zone.getID());
1102             } else {
1103                 log("fixTimeZone: skip changing zone as getAutoTimeZone was false");
1104             }
1105             saveNitzTimeZone(zone.getID());
1106         } else {
1107             log("fixTimeZone: zone == null, do nothing for zone");
1108         }
1109     }
1110
1111     /**
1112      * Query the carrier configuration to determine if there are any network overrides
1113      * for roaming or not roaming for the current service state.
1114      */
1115     protected void updateRoamingState() {
1116         ICarrierConfigLoader configLoader =
1117             (ICarrierConfigLoader) ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE);
1118         if (configLoader != null) {
1119             try {
1120                 PersistableBundle b = configLoader.getConfigForSubId(mPhone.getSubId());
1121                 String systemId = Integer.toString(mNewSS.getSystemId());
1122
1123                 if (alwaysOnHomeNetwork(b)) {
1124                     log("updateRoamingState: carrier config override always on home network");
1125                     setRoamingOff();
1126                 } else if (isNonRoamingInGsmNetwork(b, mNewSS.getOperatorNumeric())
1127                         || isNonRoamingInCdmaNetwork(b, systemId)) {
1128                     log("updateRoamingState: carrier config override set non-roaming:"
1129                             + mNewSS.getOperatorNumeric() + ", " + systemId);
1130                     setRoamingOff();
1131                 } else if (isRoamingInGsmNetwork(b, mNewSS.getOperatorNumeric())
1132                         || isRoamingInCdmaNetwork(b, systemId)) {
1133                     log("updateRoamingState: carrier config override set roaming:"
1134                             + mNewSS.getOperatorNumeric() + ", " + systemId);
1135                     setRoamingOn();
1136                 }
1137             } catch (RemoteException e) {
1138                 loge("updateRoamingState: unable to access carrier config service");
1139             }
1140         } else {
1141             log("updateRoamingState: no carrier config service available");
1142         }
1143
1144         if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
1145             mNewSS.setVoiceRoaming(true);
1146             mNewSS.setDataRoaming(true);
1147         }
1148     }
1149
1150     private void setRoamingOn() {
1151         mNewSS.setVoiceRoaming(true);
1152         mNewSS.setDataRoaming(true);
1153         mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_ON);
1154         mNewSS.setCdmaEriIconMode(EriInfo.ROAMING_ICON_MODE_NORMAL);
1155     }
1156
1157     private void setRoamingOff() {
1158         mNewSS.setVoiceRoaming(false);
1159         mNewSS.setDataRoaming(false);
1160         mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_OFF);
1161     }
1162
1163     protected void pollStateDone() {
1164         updateRoamingState();
1165
1166         useDataRegStateForDataOnlyDevices();
1167         resetServiceStateInIwlanMode();
1168         if (DBG) log("pollStateDone: cdma oldSS=[" + mSS + "] newSS=[" + mNewSS + "]");
1169
1170         boolean hasRegistered =
1171             mSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE
1172             && mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE;
1173
1174         boolean hasDeregistered =
1175             mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE
1176             && mNewSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE;
1177
1178         boolean hasCdmaDataConnectionAttached =
1179             mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE
1180             && mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE;
1181
1182         boolean hasCdmaDataConnectionDetached =
1183             mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE
1184             && mNewSS.getDataRegState() != ServiceState.STATE_IN_SERVICE;
1185
1186         boolean hasCdmaDataConnectionChanged =
1187                        mSS.getDataRegState() != mNewSS.getDataRegState();
1188
1189         boolean hasRilVoiceRadioTechnologyChanged =
1190                 mSS.getRilVoiceRadioTechnology() != mNewSS.getRilVoiceRadioTechnology();
1191
1192         boolean hasRilDataRadioTechnologyChanged =
1193                 mSS.getRilDataRadioTechnology() != mNewSS.getRilDataRadioTechnology();
1194
1195         boolean hasChanged = !mNewSS.equals(mSS);
1196
1197         boolean hasVoiceRoamingOn = !mSS.getVoiceRoaming() && mNewSS.getVoiceRoaming();
1198
1199         boolean hasVoiceRoamingOff = mSS.getVoiceRoaming() && !mNewSS.getVoiceRoaming();
1200
1201         boolean hasDataRoamingOn = !mSS.getDataRoaming() && mNewSS.getDataRoaming();
1202
1203         boolean hasDataRoamingOff = mSS.getDataRoaming() && !mNewSS.getDataRoaming();
1204
1205         boolean hasLocationChanged = !mNewCellLoc.equals(mCellLoc);
1206
1207         TelephonyManager tm =
1208                 (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
1209
1210         // Add an event log when connection state changes
1211         if (mSS.getVoiceRegState() != mNewSS.getVoiceRegState() ||
1212                 mSS.getDataRegState() != mNewSS.getDataRegState()) {
1213             EventLog.writeEvent(EventLogTags.CDMA_SERVICE_STATE_CHANGE,
1214                     mSS.getVoiceRegState(), mSS.getDataRegState(),
1215                     mNewSS.getVoiceRegState(), mNewSS.getDataRegState());
1216         }
1217
1218         ServiceState tss;
1219         tss = mSS;
1220         mSS = mNewSS;
1221         mNewSS = tss;
1222         // clean slate for next time
1223         mNewSS.setStateOutOfService();
1224
1225         CdmaCellLocation tcl = mCellLoc;
1226         mCellLoc = mNewCellLoc;
1227         mNewCellLoc = tcl;
1228
1229         if (hasRilVoiceRadioTechnologyChanged) {
1230             updatePhoneObject();
1231         }
1232
1233         if (hasRilDataRadioTechnologyChanged) {
1234             tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), mSS.getRilDataRadioTechnology());
1235
1236             if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
1237                         == mSS.getRilDataRadioTechnology()) {
1238                 log("pollStateDone: IWLAN enabled");
1239             }
1240         }
1241
1242         if (hasRegistered) {
1243             mNetworkAttachedRegistrants.notifyRegistrants();
1244         }
1245
1246         if (hasChanged) {
1247             if ((mCi.getRadioState().isOn()) && (!mIsSubscriptionFromRuim)) {
1248                 String eriText;
1249                 // Now the CDMAPhone sees the new ServiceState so it can get the new ERI text
1250                 if (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
1251                     eriText = mPhone.getCdmaEriText();
1252                 } else {
1253                     // Note that ServiceState.STATE_OUT_OF_SERVICE is valid used for
1254                     // mRegistrationState 0,2,3 and 4
1255                     eriText = mPhone.getContext().getText(
1256                             com.android.internal.R.string.roamingTextSearching).toString();
1257                 }
1258                 mSS.setOperatorAlphaLong(eriText);
1259             }
1260
1261             String operatorNumeric;
1262
1263             tm.setNetworkOperatorNameForPhone(mPhone.getPhoneId(), mSS.getOperatorAlphaLong());
1264
1265             String prevOperatorNumeric = tm.getNetworkOperatorForPhone(mPhone.getPhoneId());
1266             operatorNumeric = mSS.getOperatorNumeric();
1267
1268             // try to fix the invalid Operator Numeric
1269             if (isInvalidOperatorNumeric(operatorNumeric)) {
1270                 int sid = mSS.getSystemId();
1271                 operatorNumeric = fixUnknownMcc(operatorNumeric, sid);
1272             }
1273
1274             tm.setNetworkOperatorNumericForPhone(mPhone.getPhoneId(), operatorNumeric);
1275             updateCarrierMccMncConfiguration(operatorNumeric,
1276                     prevOperatorNumeric, mPhone.getContext());
1277
1278             if (isInvalidOperatorNumeric(operatorNumeric)) {
1279                 if (DBG) log("operatorNumeric "+ operatorNumeric +"is invalid");
1280                 tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), "");
1281                 mGotCountryCode = false;
1282             } else {
1283                 String isoCountryCode = "";
1284                 String mcc = operatorNumeric.substring(0, 3);
1285                 try{
1286                     isoCountryCode = MccTable.countryCodeForMcc(Integer.parseInt(
1287                             operatorNumeric.substring(0,3)));
1288                 } catch ( NumberFormatException ex){
1289                     loge("pollStateDone: countryCodeForMcc error" + ex);
1290                 } catch ( StringIndexOutOfBoundsException ex) {
1291                     loge("pollStateDone: countryCodeForMcc error" + ex);
1292                 }
1293
1294                 tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), isoCountryCode);
1295                 mGotCountryCode = true;
1296
1297                 setOperatorIdd(operatorNumeric);
1298
1299                 if (shouldFixTimeZoneNow(mPhone, operatorNumeric, prevOperatorNumeric,
1300                         mNeedFixZone)) {
1301                     fixTimeZone(isoCountryCode);
1302                 }
1303             }
1304
1305             tm.setNetworkRoamingForPhone(mPhone.getPhoneId(),
1306                     (mSS.getVoiceRoaming() || mSS.getDataRoaming()));
1307
1308             updateSpnDisplay();
1309             // set roaming type
1310             setRoamingType(mSS);
1311             log("Broadcasting ServiceState : " + mSS);
1312             mPhone.notifyServiceStateChanged(mSS);
1313         }
1314
1315         if (hasCdmaDataConnectionAttached) {
1316             mAttachedRegistrants.notifyRegistrants();
1317         }
1318
1319         if (hasCdmaDataConnectionDetached) {
1320             mDetachedRegistrants.notifyRegistrants();
1321         }
1322
1323         if (hasCdmaDataConnectionChanged || hasRilDataRadioTechnologyChanged) {
1324             notifyDataRegStateRilRadioTechnologyChanged();
1325             if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
1326                         == mSS.getRilDataRadioTechnology()) {
1327                 mPhone.notifyDataConnection(Phone.REASON_IWLAN_AVAILABLE);
1328             } else {
1329                 mPhone.notifyDataConnection(null);
1330             }
1331         }
1332
1333         if (hasVoiceRoamingOn) {
1334             mVoiceRoamingOnRegistrants.notifyRegistrants();
1335         }
1336
1337         if (hasVoiceRoamingOff) {
1338             mVoiceRoamingOffRegistrants.notifyRegistrants();
1339         }
1340
1341         if (hasDataRoamingOn) {
1342             mDataRoamingOnRegistrants.notifyRegistrants();
1343         }
1344
1345         if (hasDataRoamingOff) {
1346             mDataRoamingOffRegistrants.notifyRegistrants();
1347         }
1348
1349         if (hasLocationChanged) {
1350             mPhone.notifyLocationChanged();
1351         }
1352         // TODO: Add CdmaCellIdenity updating, see CdmaLteServiceStateTracker.
1353     }
1354
1355     protected boolean isInvalidOperatorNumeric(String operatorNumeric) {
1356         return operatorNumeric == null || operatorNumeric.length() < 5 ||
1357                     operatorNumeric.startsWith(INVALID_MCC);
1358     }
1359
1360     protected String fixUnknownMcc(String operatorNumeric, int sid) {
1361         if (sid <= 0) {
1362             // no cdma information is available, do nothing
1363             return operatorNumeric;
1364         }
1365
1366         // resolve the mcc from sid;
1367         // if mSavedTimeZone is null, TimeZone would get the default timeZone,
1368         // and the fixTimeZone couldn't help, because it depends on operator Numeric;
1369         // if the sid is conflict and timezone is unavailable, the mcc may be not right.
1370         boolean isNitzTimeZone = false;
1371         int timeZone = 0;
1372         TimeZone tzone = null;
1373         if (mSavedTimeZone != null) {
1374              timeZone =
1375                      TimeZone.getTimeZone(mSavedTimeZone).getRawOffset()/MS_PER_HOUR;
1376              isNitzTimeZone = true;
1377         } else {
1378              tzone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime);
1379              if (tzone != null)
1380                      timeZone = tzone.getRawOffset()/MS_PER_HOUR;
1381         }
1382
1383         int mcc = mHbpcdUtils.getMcc(sid,
1384                 timeZone, (mZoneDst ? 1 : 0), isNitzTimeZone);
1385         if (mcc > 0) {
1386             operatorNumeric = Integer.toString(mcc) + DEFAULT_MNC;
1387         }
1388         return operatorNumeric;
1389     }
1390
1391     protected void setOperatorIdd(String operatorNumeric) {
1392         // Retrieve the current country information
1393         // with the MCC got from opeatorNumeric.
1394         String idd = mHbpcdUtils.getIddByMcc(
1395                 Integer.parseInt(operatorNumeric.substring(0,3)));
1396         if (idd != null && !idd.isEmpty()) {
1397             mPhone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING,
1398                      idd);
1399         } else {
1400             // use default "+", since we don't know the current IDP
1401             mPhone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING, "+");
1402         }
1403     }
1404
1405     /**
1406      * Returns a TimeZone object based only on parameters from the NITZ string.
1407      */
1408     private TimeZone getNitzTimeZone(int offset, boolean dst, long when) {
1409         TimeZone guess = findTimeZone(offset, dst, when);
1410         if (guess == null) {
1411             // Couldn't find a proper timezone.  Perhaps the DST data is wrong.
1412             guess = findTimeZone(offset, !dst, when);
1413         }
1414         if (DBG) log("getNitzTimeZone returning " + (guess == null ? guess : guess.getID()));
1415         return guess;
1416     }
1417
1418     private TimeZone findTimeZone(int offset, boolean dst, long when) {
1419         int rawOffset = offset;
1420         if (dst) {
1421             rawOffset -= MS_PER_HOUR;
1422         }
1423         String[] zones = TimeZone.getAvailableIDs(rawOffset);
1424         TimeZone guess = null;
1425         Date d = new Date(when);
1426         for (String zone : zones) {
1427             TimeZone tz = TimeZone.getTimeZone(zone);
1428             if (tz.getOffset(when) == offset &&
1429                     tz.inDaylightTime(d) == dst) {
1430                 guess = tz;
1431                 break;
1432             }
1433         }
1434
1435         return guess;
1436     }
1437
1438     /**
1439      * TODO: This code is exactly the same as in GsmServiceStateTracker
1440      * and has a TODO to not poll signal strength if screen is off.
1441      * This code should probably be hoisted to the base class so
1442      * the fix, when added, works for both.
1443      */
1444     private void
1445     queueNextSignalStrengthPoll() {
1446         if (mDontPollSignalStrength) {
1447             // The radio is telling us about signal strength changes
1448             // we don't have to ask it
1449             return;
1450         }
1451
1452         Message msg;
1453
1454         msg = obtainMessage();
1455         msg.what = EVENT_POLL_SIGNAL_STRENGTH;
1456
1457         // TODO Don't poll signal strength if screen is off
1458         sendMessageDelayed(msg, POLL_PERIOD_MILLIS);
1459     }
1460
1461     protected int radioTechnologyToDataServiceState(int code) {
1462         int retVal = ServiceState.STATE_OUT_OF_SERVICE;
1463         switch(code) {
1464         case 0:
1465         case 1:
1466         case 2:
1467         case 3:
1468         case 4:
1469         case 5:
1470             break;
1471         case 6: // RADIO_TECHNOLOGY_1xRTT
1472         case 7: // RADIO_TECHNOLOGY_EVDO_0
1473         case 8: // RADIO_TECHNOLOGY_EVDO_A
1474         case 12: // RADIO_TECHNOLOGY_EVDO_B
1475         case 13: // RADIO_TECHNOLOGY_EHRPD
1476             retVal = ServiceState.STATE_IN_SERVICE;
1477             break;
1478         default:
1479             loge("radioTechnologyToDataServiceState: Wrong radioTechnology code.");
1480         break;
1481         }
1482         return(retVal);
1483     }
1484
1485     /** code is registration state 0-5 from TS 27.007 7.2 */
1486     protected int
1487     regCodeToServiceState(int code) {
1488         switch (code) {
1489         case 0: // Not searching and not registered
1490             return ServiceState.STATE_OUT_OF_SERVICE;
1491         case 1:
1492             return ServiceState.STATE_IN_SERVICE;
1493         case 2: // 2 is "searching", fall through
1494         case 3: // 3 is "registration denied", fall through
1495         case 4: // 4 is "unknown", not valid in current baseband
1496             return ServiceState.STATE_OUT_OF_SERVICE;
1497         case 5:// 5 is "Registered, roaming"
1498             return ServiceState.STATE_IN_SERVICE;
1499
1500         default:
1501             loge("regCodeToServiceState: unexpected service state " + code);
1502         return ServiceState.STATE_OUT_OF_SERVICE;
1503         }
1504     }
1505
1506     @Override
1507     public int getCurrentDataConnectionState() {
1508         return mSS.getDataRegState();
1509     }
1510
1511     /**
1512      * code is registration state 0-5 from TS 27.007 7.2
1513      * returns true if registered roam, false otherwise
1514      */
1515     protected boolean
1516     regCodeIsRoaming (int code) {
1517         // 5 is  "in service -- roam"
1518         return 5 == code;
1519     }
1520
1521     /**
1522      * Determine whether a roaming indicator is in the carrier-specified list of ERIs for
1523      * home system
1524      *
1525      * @param roamInd roaming indicator in String
1526      * @return true if the roamInd is in the carrier-specified list of ERIs for home network
1527      */
1528     private boolean isRoamIndForHomeSystem(String roamInd) {
1529         // retrieve the carrier-specified list of ERIs for home system
1530         String[] homeRoamIndicators = mPhone.getContext().getResources()
1531                 .getStringArray(com.android.internal.R.array.config_cdma_home_system);
1532
1533         if (homeRoamIndicators != null) {
1534             // searches through the comma-separated list for a match,
1535             // return true if one is found.
1536             for (String homeRoamInd : homeRoamIndicators) {
1537                 if (homeRoamInd.equals(roamInd)) {
1538                     return true;
1539                 }
1540             }
1541             // no matches found against the list!
1542             return false;
1543         }
1544
1545         // no system property found for the roaming indicators for home system
1546         return false;
1547     }
1548
1549     /**
1550      * Set roaming state when cdmaRoaming is true and ons is different from spn
1551      * @param cdmaRoaming TS 27.007 7.2 CREG registered roaming
1552      * @param s ServiceState hold current ons
1553      * @return true for roaming state set
1554      */
1555     private
1556     boolean isRoamingBetweenOperators(boolean cdmaRoaming, ServiceState s) {
1557         String spn = ((TelephonyManager) mPhone.getContext().
1558                 getSystemService(Context.TELEPHONY_SERVICE)).
1559                 getSimOperatorNameForPhone(mPhoneBase.getPhoneId());
1560
1561         // NOTE: in case of RUIM we should completely ignore the ERI data file and
1562         // mOperatorAlphaLong is set from RIL_REQUEST_OPERATOR response 0 (alpha ONS)
1563         String onsl = s.getVoiceOperatorAlphaLong();
1564         String onss = s.getVoiceOperatorAlphaShort();
1565
1566         boolean equalsOnsl = onsl != null && spn.equals(onsl);
1567         boolean equalsOnss = onss != null && spn.equals(onss);
1568
1569         return cdmaRoaming && !(equalsOnsl || equalsOnss);
1570     }
1571
1572
1573     /**
1574      * nitzReceiveTime is time_t that the NITZ time was posted
1575      */
1576
1577     private
1578     void setTimeFromNITZString (String nitz, long nitzReceiveTime)
1579     {
1580         // "yy/mm/dd,hh:mm:ss(+/-)tz"
1581         // tz is in number of quarter-hours
1582
1583         long start = SystemClock.elapsedRealtime();
1584         if (DBG) {
1585             log("NITZ: " + nitz + "," + nitzReceiveTime +
1586                         " start=" + start + " delay=" + (start - nitzReceiveTime));
1587         }
1588
1589         try {
1590             /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
1591              * offset as well (which we won't worry about until later) */
1592             Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
1593
1594             c.clear();
1595             c.set(Calendar.DST_OFFSET, 0);
1596
1597             String[] nitzSubs = nitz.split("[/:,+-]");
1598
1599             int year = 2000 + Integer.parseInt(nitzSubs[0]);
1600             c.set(Calendar.YEAR, year);
1601
1602             // month is 0 based!
1603             int month = Integer.parseInt(nitzSubs[1]) - 1;
1604             c.set(Calendar.MONTH, month);
1605
1606             int date = Integer.parseInt(nitzSubs[2]);
1607             c.set(Calendar.DATE, date);
1608
1609             int hour = Integer.parseInt(nitzSubs[3]);
1610             c.set(Calendar.HOUR, hour);
1611
1612             int minute = Integer.parseInt(nitzSubs[4]);
1613             c.set(Calendar.MINUTE, minute);
1614
1615             int second = Integer.parseInt(nitzSubs[5]);
1616             c.set(Calendar.SECOND, second);
1617
1618             boolean sign = (nitz.indexOf('-') == -1);
1619
1620             int tzOffset = Integer.parseInt(nitzSubs[6]);
1621
1622             int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7])
1623                                               : 0;
1624
1625             // The zone offset received from NITZ is for current local time,
1626             // so DST correction is already applied.  Don't add it again.
1627             //
1628             // tzOffset += dst * 4;
1629             //
1630             // We could unapply it if we wanted the raw offset.
1631
1632             tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000;
1633
1634             TimeZone    zone = null;
1635
1636             // As a special extension, the Android emulator appends the name of
1637             // the host computer's timezone to the nitz string. this is zoneinfo
1638             // timezone name of the form Area!Location or Area!Location!SubLocation
1639             // so we need to convert the ! into /
1640             if (nitzSubs.length >= 9) {
1641                 String  tzname = nitzSubs[8].replace('!','/');
1642                 zone = TimeZone.getTimeZone( tzname );
1643             }
1644
1645             String iso = ((TelephonyManager) mPhone.getContext().
1646                     getSystemService(Context.TELEPHONY_SERVICE)).
1647                     getNetworkCountryIsoForPhone(mPhone.getPhoneId());
1648
1649             if (zone == null) {
1650                 if (mGotCountryCode) {
1651                     if (iso != null && iso.length() > 0) {
1652                         zone = TimeUtils.getTimeZone(tzOffset, dst != 0,
1653                                 c.getTimeInMillis(),
1654                                 iso);
1655                     } else {
1656                         // We don't have a valid iso country code.  This is
1657                         // most likely because we're on a test network that's
1658                         // using a bogus MCC (eg, "001"), so get a TimeZone
1659                         // based only on the NITZ parameters.
1660                         zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis());
1661                     }
1662                 }
1663             }
1664
1665             if ((zone == null) || (mZoneOffset != tzOffset) || (mZoneDst != (dst != 0))){
1666                 // We got the time before the country or the zone has changed
1667                 // so we don't know how to identify the DST rules yet.  Save
1668                 // the information and hope to fix it up later.
1669
1670                 mNeedFixZone = true;
1671                 mZoneOffset  = tzOffset;
1672                 mZoneDst     = dst != 0;
1673                 mZoneTime    = c.getTimeInMillis();
1674             }
1675             if (DBG) {
1676                 log("NITZ: tzOffset=" + tzOffset + " dst=" + dst + " zone=" +
1677                         (zone!=null ? zone.getID() : "NULL") +
1678                         " iso=" + iso + " mGotCountryCode=" + mGotCountryCode +
1679                         " mNeedFixZone=" + mNeedFixZone);
1680             }
1681
1682             if (zone != null) {
1683                 if (getAutoTimeZone()) {
1684                     setAndBroadcastNetworkSetTimeZone(zone.getID());
1685                 }
1686                 saveNitzTimeZone(zone.getID());
1687             }
1688
1689             String ignore = SystemProperties.get("gsm.ignore-nitz");
1690             if (ignore != null && ignore.equals("yes")) {
1691                 if (DBG) log("NITZ: Not setting clock because gsm.ignore-nitz is set");
1692                 return;
1693             }
1694
1695             try {
1696                 mWakeLock.acquire();
1697
1698                 /**
1699                  * Correct the NITZ time by how long its taken to get here.
1700                  */
1701                 long millisSinceNitzReceived
1702                         = SystemClock.elapsedRealtime() - nitzReceiveTime;
1703
1704                 if (millisSinceNitzReceived < 0) {
1705                     // Sanity check: something is wrong
1706                     if (DBG) {
1707                         log("NITZ: not setting time, clock has rolled "
1708                                         + "backwards since NITZ time was received, "
1709                                         + nitz);
1710                     }
1711                     return;
1712                 }
1713
1714                 if (millisSinceNitzReceived > Integer.MAX_VALUE) {
1715                     // If the time is this far off, something is wrong > 24 days!
1716                     if (DBG) {
1717                         log("NITZ: not setting time, processing has taken "
1718                                     + (millisSinceNitzReceived / (1000 * 60 * 60 * 24))
1719                                     + " days");
1720                     }
1721                     return;
1722                 }
1723
1724                 // Note: with range checks above, cast to int is safe
1725                 c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived);
1726
1727                 if (getAutoTime()) {
1728                     /**
1729                      * Update system time automatically
1730                      */
1731                     long gained = c.getTimeInMillis() - System.currentTimeMillis();
1732                     long timeSinceLastUpdate = SystemClock.elapsedRealtime() - mSavedAtTime;
1733                     int nitzUpdateSpacing = Settings.Global.getInt(mCr,
1734                             Settings.Global.NITZ_UPDATE_SPACING, mNitzUpdateSpacing);
1735                     int nitzUpdateDiff = Settings.Global.getInt(mCr,
1736                             Settings.Global.NITZ_UPDATE_DIFF, mNitzUpdateDiff);
1737
1738                     if ((mSavedAtTime == 0) || (timeSinceLastUpdate > nitzUpdateSpacing)
1739                             || (Math.abs(gained) > nitzUpdateDiff)) {
1740                         if (DBG) {
1741                             log("NITZ: Auto updating time of day to " + c.getTime()
1742                                 + " NITZ receive delay=" + millisSinceNitzReceived
1743                                 + "ms gained=" + gained + "ms from " + nitz);
1744                         }
1745
1746                         setAndBroadcastNetworkSetTime(c.getTimeInMillis());
1747                     } else {
1748                         if (DBG) {
1749                             log("NITZ: ignore, a previous update was "
1750                                 + timeSinceLastUpdate + "ms ago and gained=" + gained + "ms");
1751                         }
1752                         return;
1753                     }
1754                 }
1755
1756                 /**
1757                  * Update properties and save the time we did the update
1758                  */
1759                 if (DBG) log("NITZ: update nitz time property");
1760                 SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis()));
1761                 mSavedTime = c.getTimeInMillis();
1762                 mSavedAtTime = SystemClock.elapsedRealtime();
1763             } finally {
1764                 long end = SystemClock.elapsedRealtime();
1765                 if (DBG) log("NITZ: end=" + end + " dur=" + (end - start));
1766                 mWakeLock.release();
1767             }
1768         } catch (RuntimeException ex) {
1769             loge("NITZ: Parsing NITZ time " + nitz + " ex=" + ex);
1770         }
1771     }
1772
1773     private boolean getAutoTime() {
1774         try {
1775             return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME) > 0;
1776         } catch (SettingNotFoundException snfe) {
1777             return true;
1778         }
1779     }
1780
1781     private boolean getAutoTimeZone() {
1782         try {
1783             return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE) > 0;
1784         } catch (SettingNotFoundException snfe) {
1785             return true;
1786         }
1787     }
1788
1789     private void saveNitzTimeZone(String zoneId) {
1790         mSavedTimeZone = zoneId;
1791     }
1792
1793     /**
1794      * Set the timezone and send out a sticky broadcast so the system can
1795      * determine if the timezone was set by the carrier.
1796      *
1797      * @param zoneId timezone set by carrier
1798      */
1799     private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
1800         if (DBG) log("setAndBroadcastNetworkSetTimeZone: setTimeZone=" + zoneId);
1801         AlarmManager alarm =
1802             (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
1803         alarm.setTimeZone(zoneId);
1804         Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
1805         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1806         intent.putExtra("time-zone", zoneId);
1807         mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1808     }
1809
1810     /**
1811      * Set the time and Send out a sticky broadcast so the system can determine
1812      * if the time was set by the carrier.
1813      *
1814      * @param time time set by network
1815      */
1816     private void setAndBroadcastNetworkSetTime(long time) {
1817         if (DBG) log("setAndBroadcastNetworkSetTime: time=" + time + "ms");
1818         SystemClock.setCurrentTimeMillis(time);
1819         Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
1820         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1821         intent.putExtra("time", time);
1822         mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1823     }
1824
1825     private void revertToNitzTime() {
1826         if (Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME, 0) == 0) {
1827             return;
1828         }
1829         if (DBG) {
1830             log("revertToNitzTime: mSavedTime=" + mSavedTime + " mSavedAtTime=" + mSavedAtTime);
1831         }
1832         if (mSavedTime != 0 && mSavedAtTime != 0) {
1833             setAndBroadcastNetworkSetTime(mSavedTime
1834                     + (SystemClock.elapsedRealtime() - mSavedAtTime));
1835         }
1836     }
1837
1838     private void revertToNitzTimeZone() {
1839         if (Settings.Global.getInt(mPhone.getContext().getContentResolver(),
1840                 Settings.Global.AUTO_TIME_ZONE, 0) == 0) {
1841             return;
1842         }
1843         if (DBG) log("revertToNitzTimeZone: tz='" + mSavedTimeZone);
1844         if (mSavedTimeZone != null) {
1845             setAndBroadcastNetworkSetTimeZone(mSavedTimeZone);
1846         }
1847     }
1848
1849     protected boolean isSidsAllZeros() {
1850         if (mHomeSystemId != null) {
1851             for (int i=0; i < mHomeSystemId.length; i++) {
1852                 if (mHomeSystemId[i] != 0) {
1853                     return false;
1854                 }
1855             }
1856         }
1857         return true;
1858     }
1859
1860     /**
1861      * Check whether a specified system ID that matches one of the home system IDs.
1862      */
1863     private boolean isHomeSid(int sid) {
1864         if (mHomeSystemId != null) {
1865             for (int i=0; i < mHomeSystemId.length; i++) {
1866                 if (sid == mHomeSystemId[i]) {
1867                     return true;
1868                 }
1869             }
1870         }
1871         return false;
1872     }
1873
1874     /**
1875      * @return true if phone is camping on a technology
1876      * that could support voice and data simultaneously.
1877      */
1878     @Override
1879     public boolean isConcurrentVoiceAndDataAllowed() {
1880         // Note: it needs to be confirmed which CDMA network types
1881         // can support voice and data calls concurrently.
1882         // For the time-being, the return value will be false.
1883         return false;
1884     }
1885
1886     public String getMdnNumber() {
1887         return mMdn;
1888     }
1889
1890     public String getCdmaMin() {
1891          return mMin;
1892     }
1893
1894     /** Returns null if NV is not yet ready */
1895     public String getPrlVersion() {
1896         return mPrlVersion;
1897     }
1898
1899     /**
1900      * Returns IMSI as MCC + MNC + MIN
1901      */
1902     String getImsi() {
1903         // TODO: When RUIM is enabled, IMSI will come from RUIM not build-time props.
1904         String operatorNumeric = ((TelephonyManager) mPhone.getContext().
1905                 getSystemService(Context.TELEPHONY_SERVICE)).
1906                 getSimOperatorNumericForPhone(mPhoneBase.getPhoneId());
1907
1908         if (!TextUtils.isEmpty(operatorNumeric) && getCdmaMin() != null) {
1909             return (operatorNumeric + getCdmaMin());
1910         } else {
1911             return null;
1912         }
1913     }
1914
1915     /**
1916      * Check if subscription data has been assigned to mMin
1917      *
1918      * return true if MIN info is ready; false otherwise.
1919      */
1920     public boolean isMinInfoReady() {
1921         return mIsMinInfoReady;
1922     }
1923
1924     /**
1925      * Returns OTASP_UNKNOWN, OTASP_NEEDED or OTASP_NOT_NEEDED
1926      */
1927     int getOtasp() {
1928         int provisioningState;
1929         // for ruim, min is null means require otasp.
1930         if (mIsSubscriptionFromRuim && mMin == null) {
1931             return OTASP_NEEDED;
1932         }
1933         if (mMin == null || (mMin.length() < 6)) {
1934             if (DBG) log("getOtasp: bad mMin='" + mMin + "'");
1935             provisioningState = OTASP_UNKNOWN;
1936         } else {
1937             if ((mMin.equals(UNACTIVATED_MIN_VALUE)
1938                     || mMin.substring(0,6).equals(UNACTIVATED_MIN2_VALUE))
1939                     || SystemProperties.getBoolean("test_cdma_setup", false)) {
1940                 provisioningState = OTASP_NEEDED;
1941             } else {
1942                 provisioningState = OTASP_NOT_NEEDED;
1943             }
1944         }
1945         if (DBG) log("getOtasp: state=" + provisioningState);
1946         return provisioningState;
1947     }
1948
1949     @Override
1950     protected void hangupAndPowerOff() {
1951         // hang up all active voice calls
1952         mPhone.mCT.mRingingCall.hangupIfAlive();
1953         mPhone.mCT.mBackgroundCall.hangupIfAlive();
1954         mPhone.mCT.mForegroundCall.hangupIfAlive();
1955         mCi.setRadioPower(false, null);
1956     }
1957
1958     protected void parseSidNid (String sidStr, String nidStr) {
1959         if (sidStr != null) {
1960             String[] sid = sidStr.split(",");
1961             mHomeSystemId = new int[sid.length];
1962             for (int i = 0; i < sid.length; i++) {
1963                 try {
1964                     mHomeSystemId[i] = Integer.parseInt(sid[i]);
1965                 } catch (NumberFormatException ex) {
1966                     loge("error parsing system id: " + ex);
1967                 }
1968             }
1969         }
1970         if (DBG) log("CDMA_SUBSCRIPTION: SID=" + sidStr);
1971
1972         if (nidStr != null) {
1973             String[] nid = nidStr.split(",");
1974             mHomeNetworkId = new int[nid.length];
1975             for (int i = 0; i < nid.length; i++) {
1976                 try {
1977                     mHomeNetworkId[i] = Integer.parseInt(nid[i]);
1978                 } catch (NumberFormatException ex) {
1979                     loge("CDMA_SUBSCRIPTION: error parsing network id: " + ex);
1980                 }
1981             }
1982         }
1983         if (DBG) log("CDMA_SUBSCRIPTION: NID=" + nidStr);
1984     }
1985
1986     protected void updateOtaspState() {
1987         int otaspMode = getOtasp();
1988         int oldOtaspMode = mCurrentOtaspMode;
1989         mCurrentOtaspMode = otaspMode;
1990
1991         // Notify apps subscription info is ready
1992         if (mCdmaForSubscriptionInfoReadyRegistrants != null) {
1993             if (DBG) log("CDMA_SUBSCRIPTION: call notifyRegistrants()");
1994             mCdmaForSubscriptionInfoReadyRegistrants.notifyRegistrants();
1995         }
1996         if (oldOtaspMode != mCurrentOtaspMode) {
1997             if (DBG) {
1998                 log("CDMA_SUBSCRIPTION: call notifyOtaspChanged old otaspMode=" +
1999                     oldOtaspMode + " new otaspMode=" + mCurrentOtaspMode);
2000             }
2001             mPhone.notifyOtaspChanged(mCurrentOtaspMode);
2002         }
2003     }
2004
2005     protected UiccCardApplication getUiccCardApplication() {
2006             return  mUiccController.getUiccCardApplication(mPhone.getPhoneId(),
2007                     UiccController.APP_FAM_3GPP2);
2008     }
2009
2010     @Override
2011     protected void onUpdateIccAvailability() {
2012         if (mUiccController == null ) {
2013             return;
2014         }
2015
2016         UiccCardApplication newUiccApplication = getUiccCardApplication();
2017
2018         if (mUiccApplcation != newUiccApplication) {
2019             if (mUiccApplcation != null) {
2020                 log("Removing stale icc objects.");
2021                 mUiccApplcation.unregisterForReady(this);
2022                 if (mIccRecords != null) {
2023                     mIccRecords.unregisterForRecordsLoaded(this);
2024                 }
2025                 mIccRecords = null;
2026                 mUiccApplcation = null;
2027             }
2028             if (newUiccApplication != null) {
2029                 log("New card found");
2030                 mUiccApplcation = newUiccApplication;
2031                 mIccRecords = mUiccApplcation.getIccRecords();
2032                 if (mIsSubscriptionFromRuim) {
2033                     mUiccApplcation.registerForReady(this, EVENT_RUIM_READY, null);
2034                     if (mIccRecords != null) {
2035                         mIccRecords.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null);
2036                     }
2037                 }
2038             }
2039         }
2040     }
2041
2042     @Override
2043     protected void log(String s) {
2044         Rlog.d(LOG_TAG, "[CdmaSST] " + s);
2045     }
2046
2047     @Override
2048     protected void loge(String s) {
2049         Rlog.e(LOG_TAG, "[CdmaSST] " + s);
2050     }
2051
2052     @Override
2053     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2054         pw.println("CdmaServiceStateTracker extends:");
2055         super.dump(fd, pw, args);
2056         pw.flush();
2057         pw.println(" mPhone=" + mPhone);
2058         pw.println(" mSS=" + mSS);
2059         pw.println(" mNewSS=" + mNewSS);
2060         pw.println(" mCellLoc=" + mCellLoc);
2061         pw.println(" mNewCellLoc=" + mNewCellLoc);
2062         pw.println(" mCurrentOtaspMode=" + mCurrentOtaspMode);
2063         pw.println(" mRoamingIndicator=" + mRoamingIndicator);
2064         pw.println(" mIsInPrl=" + mIsInPrl);
2065         pw.println(" mDefaultRoamingIndicator=" + mDefaultRoamingIndicator);
2066         pw.println(" mRegistrationState=" + mRegistrationState);
2067         pw.println(" mNeedFixZone=" + mNeedFixZone);
2068         pw.flush();
2069         pw.println(" mZoneOffset=" + mZoneOffset);
2070         pw.println(" mZoneDst=" + mZoneDst);
2071         pw.println(" mZoneTime=" + mZoneTime);
2072         pw.println(" mGotCountryCode=" + mGotCountryCode);
2073         pw.println(" mSavedTimeZone=" + mSavedTimeZone);
2074         pw.println(" mSavedTime=" + mSavedTime);
2075         pw.println(" mSavedAtTime=" + mSavedAtTime);
2076         pw.println(" mWakeLock=" + mWakeLock);
2077         pw.println(" mCurPlmn=" + mCurPlmn);
2078         pw.println(" mMdn=" + mMdn);
2079         pw.println(" mHomeSystemId=" + mHomeSystemId);
2080         pw.println(" mHomeNetworkId=" + mHomeNetworkId);
2081         pw.println(" mMin=" + mMin);
2082         pw.println(" mPrlVersion=" + mPrlVersion);
2083         pw.println(" mIsMinInfoReady=" + mIsMinInfoReady);
2084         pw.println(" mIsEriTextLoaded=" + mIsEriTextLoaded);
2085         pw.println(" mIsSubscriptionFromRuim=" + mIsSubscriptionFromRuim);
2086         pw.println(" mCdmaSSM=" + mCdmaSSM);
2087         pw.println(" mRegistrationDeniedReason=" + mRegistrationDeniedReason);
2088         pw.println(" mCurrentCarrier=" + mCurrentCarrier);
2089         pw.flush();
2090     }
2091
2092     @Override
2093     public void setImsRegistrationState(boolean registered) {
2094         log("ImsRegistrationState - registered : " + registered);
2095
2096         if (mImsRegistrationOnOff && !registered) {
2097             if (mAlarmSwitch) {
2098                 mImsRegistrationOnOff = registered;
2099
2100                 Context context = mPhone.getContext();
2101                 AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
2102                 am.cancel(mRadioOffIntent);
2103                 mAlarmSwitch = false;
2104
2105                 sendMessage(obtainMessage(EVENT_CHANGE_IMS_STATE));
2106                 return;
2107             }
2108         }
2109         mImsRegistrationOnOff = registered;
2110     }
2111 }