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