• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.telephony.cdma;
18 
19 import com.android.internal.telephony.CommandException;
20 import com.android.internal.telephony.CommandsInterface;
21 import com.android.internal.telephony.DataConnectionTracker;
22 import com.android.internal.telephony.EventLogTags;
23 import com.android.internal.telephony.IccCard;
24 import com.android.internal.telephony.MccTable;
25 import com.android.internal.telephony.Phone;
26 import com.android.internal.telephony.ServiceStateTracker;
27 import com.android.internal.telephony.TelephonyIntents;
28 import com.android.internal.telephony.TelephonyProperties;
29 
30 import android.app.AlarmManager;
31 import android.content.ContentResolver;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.database.ContentObserver;
35 import android.os.AsyncResult;
36 import android.os.Handler;
37 import android.os.Message;
38 import android.os.PowerManager;
39 import android.os.Registrant;
40 import android.os.RegistrantList;
41 import android.os.SystemClock;
42 import android.os.SystemProperties;
43 import android.provider.Settings;
44 import android.provider.Settings.SettingNotFoundException;
45 import android.provider.Telephony.Intents;
46 import android.telephony.ServiceState;
47 import android.telephony.SignalStrength;
48 import android.telephony.cdma.CdmaCellLocation;
49 import android.text.TextUtils;
50 import android.util.EventLog;
51 import android.util.Log;
52 import android.util.TimeUtils;
53 
54 import java.util.Arrays;
55 import java.util.Calendar;
56 import java.util.Date;
57 import java.util.TimeZone;
58 
59 /**
60  * {@hide}
61  */
62 public class CdmaServiceStateTracker extends ServiceStateTracker {
63     static final String LOG_TAG = "CDMA";
64 
65     CDMAPhone phone;
66     CdmaCellLocation cellLoc;
67     CdmaCellLocation newCellLoc;
68 
69     // Min values used to by getOtasp()
70     private static final String UNACTIVATED_MIN2_VALUE = "000000";
71     private static final String UNACTIVATED_MIN_VALUE = "1111110111";
72 
73     // Current Otasp value
74     int mCurrentOtaspMode = OTASP_UNINITIALIZED;
75 
76      /** if time between NITZ updates is less than mNitzUpdateSpacing the update may be ignored. */
77     private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10;
78     private int mNitzUpdateSpacing = SystemProperties.getInt("ro.nitz_update_spacing",
79             NITZ_UPDATE_SPACING_DEFAULT);
80 
81     /** If mNitzUpdateSpacing hasn't been exceeded but update is > mNitzUpdate do the update */
82     private static final int NITZ_UPDATE_DIFF_DEFAULT = 2000;
83     private int mNitzUpdateDiff = SystemProperties.getInt("ro.nitz_update_diff",
84             NITZ_UPDATE_DIFF_DEFAULT);
85 
86     /**
87      *  Values correspond to ServiceState.RADIO_TECHNOLOGY_ definitions.
88      */
89     protected int networkType = 0;
90     protected int newNetworkType = 0;
91 
92     private boolean mCdmaRoaming = false;
93     private int mRoamingIndicator;
94     private boolean mIsInPrl;
95     private int mDefaultRoamingIndicator;
96 
97     /**
98      * Initially assume no data connection.
99      */
100     protected int mDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
101     protected int mNewDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
102     protected int mRegistrationState = -1;
103     protected RegistrantList cdmaForSubscriptionInfoReadyRegistrants = new RegistrantList();
104 
105     /**
106      * Sometimes we get the NITZ time before we know what country we
107      * are in. Keep the time zone information from the NITZ string so
108      * we can fix the time zone once know the country.
109      */
110     protected boolean mNeedFixZone = false;
111     private int mZoneOffset;
112     private boolean mZoneDst;
113     private long mZoneTime;
114     protected boolean mGotCountryCode = false;
115     String mSavedTimeZone;
116     long mSavedTime;
117     long mSavedAtTime;
118 
119     /**
120      * We can't register for SIM_RECORDS_LOADED immediately because the
121      * SIMRecords object may not be instantiated yet.
122      */
123     private boolean mNeedToRegForRuimLoaded = false;
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     /** Contains the name of the registered network in CDMA (either ONS or ERI text). */
130     protected String mCurPlmn = null;
131 
132     protected String mMdn;
133     protected int mHomeSystemId[] = null;
134     protected int mHomeNetworkId[] = null;
135     protected String mMin;
136     protected String mPrlVersion;
137     protected boolean mIsMinInfoReady = false;
138 
139     private boolean isEriTextLoaded = false;
140     protected boolean isSubscriptionFromRuim = false;
141 
142     /* Used only for debugging purposes. */
143     private String mRegistrationDeniedReason;
144 
145     private ContentResolver cr;
146     private String currentCarrier = null;
147 
148     private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
149         @Override
150         public void onChange(boolean selfChange) {
151             if (DBG) log("Auto time state changed");
152             revertToNitzTime();
153         }
154     };
155 
156     private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) {
157         @Override
158         public void onChange(boolean selfChange) {
159             if (DBG) log("Auto time zone state changed");
160             revertToNitzTimeZone();
161         }
162     };
163 
CdmaServiceStateTracker(CDMAPhone phone)164     public CdmaServiceStateTracker(CDMAPhone phone) {
165         super();
166 
167         this.phone = phone;
168         cr = phone.getContext().getContentResolver();
169         cm = phone.mCM;
170         ss = new ServiceState();
171         newSS = new ServiceState();
172         cellLoc = new CdmaCellLocation();
173         newCellLoc = new CdmaCellLocation();
174         mSignalStrength = new SignalStrength();
175 
176         PowerManager powerManager =
177                 (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE);
178         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
179 
180         cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
181         cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
182 
183         cm.registerForVoiceNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED_CDMA, null);
184         cm.setOnNITZTime(this, EVENT_NITZ_TIME, null);
185         cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
186 
187         cm.registerForRUIMReady(this, EVENT_RUIM_READY, null);
188 
189         cm.registerForNVReady(this, EVENT_NV_READY, null);
190         phone.registerForEriFileLoaded(this, EVENT_ERI_FILE_LOADED, null);
191         cm.registerForCdmaOtaProvision(this,EVENT_OTA_PROVISION_STATUS_CHANGE, null);
192 
193         // System setting property AIRPLANE_MODE_ON is set in Settings.
194         int airplaneMode = Settings.System.getInt(cr, Settings.System.AIRPLANE_MODE_ON, 0);
195         mDesiredPowerState = ! (airplaneMode > 0);
196 
197         cr.registerContentObserver(
198                 Settings.System.getUriFor(Settings.System.AUTO_TIME), true,
199                 mAutoTimeObserver);
200         cr.registerContentObserver(
201             Settings.System.getUriFor(Settings.System.AUTO_TIME_ZONE), true,
202             mAutoTimeZoneObserver);
203         setSignalStrengthDefaultValues();
204 
205         mNeedToRegForRuimLoaded = true;
206     }
207 
dispose()208     public void dispose() {
209         // Unregister for all events.
210         cm.unregisterForAvailable(this);
211         cm.unregisterForRadioStateChanged(this);
212         cm.unregisterForVoiceNetworkStateChanged(this);
213         cm.unregisterForRUIMReady(this);
214         cm.unregisterForNVReady(this);
215         cm.unregisterForCdmaOtaProvision(this);
216         phone.unregisterForEriFileLoaded(this);
217         phone.mIccRecords.unregisterForRecordsLoaded(this);
218         cm.unSetOnSignalStrengthUpdate(this);
219         cm.unSetOnNITZTime(this);
220         cr.unregisterContentObserver(mAutoTimeObserver);
221         cr.unregisterContentObserver(mAutoTimeZoneObserver);
222     }
223 
224     @Override
finalize()225     protected void finalize() {
226         if (DBG) log("CdmaServiceStateTracker finalized");
227     }
228 
229     /**
230      * Registration point for subscription info ready
231      * @param h handler to notify
232      * @param what what code of message when delivered
233      * @param obj placed in Message.obj
234      */
registerForSubscriptionInfoReady(Handler h, int what, Object obj)235     public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
236         Registrant r = new Registrant(h, what, obj);
237         cdmaForSubscriptionInfoReadyRegistrants.add(r);
238 
239         if (isMinInfoReady()) {
240             r.notifyRegistrant();
241         }
242     }
243 
unregisterForSubscriptionInfoReady(Handler h)244     public void unregisterForSubscriptionInfoReady(Handler h) {
245         cdmaForSubscriptionInfoReadyRegistrants.remove(h);
246     }
247 
248     @Override
handleMessage(Message msg)249     public void handleMessage (Message msg) {
250         AsyncResult ar;
251         int[] ints;
252         String[] strings;
253 
254         switch (msg.what) {
255         case EVENT_RADIO_AVAILABLE:
256             if (DBG) log("handleMessage: EVENT_RADIO_AVAILABLE");
257             break;
258 
259         case EVENT_RUIM_READY:
260             // The RUIM is now ready i.e if it was locked it has been
261             // unlocked. At this stage, the radio is already powered on.
262             isSubscriptionFromRuim = true;
263             if (mNeedToRegForRuimLoaded) {
264                 phone.mIccRecords.registerForRecordsLoaded(this,
265                         EVENT_RUIM_RECORDS_LOADED, null);
266                 mNeedToRegForRuimLoaded = false;
267             }
268 
269             cm.getCDMASubscription(obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
270             if (DBG) log("handleMessage: EVENT_RUIM_READY, Send Request getCDMASubscription.");
271 
272             // Restore the previous network selection.
273             pollState();
274 
275             // Signal strength polling stops when radio is off.
276             queueNextSignalStrengthPoll();
277             break;
278 
279         case EVENT_NV_READY:
280             isSubscriptionFromRuim = false;
281             // For Non-RUIM phones, the subscription information is stored in
282             // Non Volatile. Here when Non-Volatile is ready, we can poll the CDMA
283             // subscription info.
284             if (DBG) log("handleMessage: EVENT_NV_READY, Send Request getCDMASubscription.");
285             cm.getCDMASubscription( obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
286             pollState();
287             // Signal strength polling stops when radio is off.
288             queueNextSignalStrengthPoll();
289             break;
290 
291         case EVENT_RADIO_STATE_CHANGED:
292             // This will do nothing in the 'radio not available' case.
293             setPowerStateToDesired();
294             pollState();
295             break;
296 
297         case EVENT_NETWORK_STATE_CHANGED_CDMA:
298             pollState();
299             break;
300 
301         case EVENT_GET_SIGNAL_STRENGTH:
302             // This callback is called when signal strength is polled
303             // all by itself.
304 
305             if (!(cm.getRadioState().isOn()) || (cm.getRadioState().isGsm())) {
306                 // Polling will continue when radio turns back on.
307                 return;
308             }
309             ar = (AsyncResult) msg.obj;
310             onSignalStrengthResult(ar);
311             queueNextSignalStrengthPoll();
312 
313             break;
314 
315         case EVENT_GET_LOC_DONE_CDMA:
316             ar = (AsyncResult) msg.obj;
317 
318             if (ar.exception == null) {
319                 String states[] = (String[])ar.result;
320                 int baseStationId = -1;
321                 int baseStationLatitude = CdmaCellLocation.INVALID_LAT_LONG;
322                 int baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
323                 int systemId = -1;
324                 int networkId = -1;
325 
326                 if (states.length > 9) {
327                     try {
328                         if (states[4] != null) {
329                             baseStationId = Integer.parseInt(states[4]);
330                         }
331                         if (states[5] != null) {
332                             baseStationLatitude = Integer.parseInt(states[5]);
333                         }
334                         if (states[6] != null) {
335                             baseStationLongitude = Integer.parseInt(states[6]);
336                         }
337                         // Some carriers only return lat-lngs of 0,0
338                         if (baseStationLatitude == 0 && baseStationLongitude == 0) {
339                             baseStationLatitude  = CdmaCellLocation.INVALID_LAT_LONG;
340                             baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
341                         }
342                         if (states[8] != null) {
343                             systemId = Integer.parseInt(states[8]);
344                         }
345                         if (states[9] != null) {
346                             networkId = Integer.parseInt(states[9]);
347                         }
348                     } catch (NumberFormatException ex) {
349                         loge("error parsing cell location data: " + ex);
350                     }
351                 }
352 
353                 cellLoc.setCellLocationData(baseStationId, baseStationLatitude,
354                         baseStationLongitude, systemId, networkId);
355                 phone.notifyLocationChanged();
356             }
357 
358             // Release any temporary cell lock, which could have been
359             // acquired to allow a single-shot location update.
360             disableSingleLocationUpdate();
361             break;
362 
363         case EVENT_POLL_STATE_REGISTRATION_CDMA:
364         case EVENT_POLL_STATE_OPERATOR_CDMA:
365             ar = (AsyncResult) msg.obj;
366             handlePollStateResult(msg.what, ar);
367             break;
368 
369         case EVENT_POLL_STATE_CDMA_SUBSCRIPTION: // Handle RIL_CDMA_SUBSCRIPTION
370             ar = (AsyncResult) msg.obj;
371 
372             if (ar.exception == null) {
373                 String cdmaSubscription[] = (String[])ar.result;
374                 if (cdmaSubscription != null && cdmaSubscription.length >= 5) {
375                     mMdn = cdmaSubscription[0];
376                     parseSidNid(cdmaSubscription[1], cdmaSubscription[2]);
377 
378                     mMin = cdmaSubscription[3];
379                     mPrlVersion = cdmaSubscription[4];
380                     if (DBG) log("GET_CDMA_SUBSCRIPTION: MDN=" + mMdn);
381 
382                     mIsMinInfoReady = true;
383 
384                     updateOtaspState();
385                     phone.getIccCard().broadcastIccStateChangedIntent(IccCard.INTENT_VALUE_ICC_IMSI,
386                             null);
387                 } else {
388                     if (DBG) {
389                         log("GET_CDMA_SUBSCRIPTION: error parsing cdmaSubscription params num="
390                             + cdmaSubscription.length);
391                     }
392                 }
393             }
394             break;
395 
396         case EVENT_POLL_SIGNAL_STRENGTH:
397             // Just poll signal strength...not part of pollState()
398 
399             cm.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH));
400             break;
401 
402         case EVENT_NITZ_TIME:
403             ar = (AsyncResult) msg.obj;
404 
405             String nitzString = (String)((Object[])ar.result)[0];
406             long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue();
407 
408             setTimeFromNITZString(nitzString, nitzReceiveTime);
409             break;
410 
411         case EVENT_SIGNAL_STRENGTH_UPDATE:
412             // This is a notification from CommandsInterface.setOnSignalStrengthUpdate.
413 
414             ar = (AsyncResult) msg.obj;
415 
416             // The radio is telling us about signal strength changes,
417             // so we don't have to ask it.
418             dontPollSignalStrength = true;
419 
420             onSignalStrengthResult(ar);
421             break;
422 
423         case EVENT_RUIM_RECORDS_LOADED:
424             updateSpnDisplay();
425             break;
426 
427         case EVENT_LOCATION_UPDATES_ENABLED:
428             ar = (AsyncResult) msg.obj;
429 
430             if (ar.exception == null) {
431                 cm.getVoiceRegistrationState(obtainMessage(EVENT_GET_LOC_DONE_CDMA, null));
432             }
433             break;
434 
435         case EVENT_ERI_FILE_LOADED:
436             // Repoll the state once the ERI file has been loaded.
437             if (DBG) log("[CdmaServiceStateTracker] ERI file has been loaded, repolling.");
438             pollState();
439             break;
440 
441         case EVENT_OTA_PROVISION_STATUS_CHANGE:
442             ar = (AsyncResult)msg.obj;
443             if (ar.exception == null) {
444                 ints = (int[]) ar.result;
445                 int otaStatus = ints[0];
446                 if (otaStatus == Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED
447                     || otaStatus == Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED) {
448                     if (DBG) log("EVENT_OTA_PROVISION_STATUS_CHANGE: Complete, Reload MDN");
449                     cm.getCDMASubscription( obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
450                 }
451             }
452             break;
453 
454         default:
455             super.handleMessage(msg);
456         break;
457         }
458     }
459 
460     //***** Private Instance Methods
461 
462     @Override
setPowerStateToDesired()463     protected void setPowerStateToDesired() {
464         // If we want it on and it's off, turn it on
465         if (mDesiredPowerState
466             && cm.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
467             cm.setRadioPower(true, null);
468         } else if (!mDesiredPowerState && cm.getRadioState().isOn()) {
469             DataConnectionTracker dcTracker = phone.mDataConnectionTracker;
470 
471             // If it's on and available and we want it off gracefully
472             powerOffRadioSafely(dcTracker);
473         } // Otherwise, we're in the desired state
474     }
475 
476     @Override
updateSpnDisplay()477     protected void updateSpnDisplay() {
478         // TODO RUIM SPN is not implemented, EF_SPN has to be read and Display Condition
479         //   Character Encoding, Language Indicator and SPN has to be set, something like below:
480         // if (cm.getRadioState().isRUIMReady()) {
481         //     rule = phone.mRuimRecords.getDisplayRule(ss.getOperatorNumeric());
482         //     spn = phone.mSIMRecords.getServiceProvideName();
483         // }
484 
485         // mOperatorAlphaLong contains the ERI text
486         String plmn = ss.getOperatorAlphaLong();
487         if (!TextUtils.equals(plmn, mCurPlmn)) {
488             // Allow A blank plmn, "" to set showPlmn to true. Previously, we
489             // would set showPlmn to true only if plmn was not empty, i.e. was not
490             // null and not blank. But this would cause us to incorrectly display
491             // "No Service". Now showPlmn is set to true for any non null string.
492             boolean showPlmn = plmn != null;
493             if (DBG) {
494                 log(String.format("updateSpnDisplay: changed sending intent" +
495                             " showPlmn='%b' plmn='%s'", showPlmn, plmn));
496             }
497             Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION);
498             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
499             intent.putExtra(Intents.EXTRA_SHOW_SPN, false);
500             intent.putExtra(Intents.EXTRA_SPN, "");
501             intent.putExtra(Intents.EXTRA_SHOW_PLMN, showPlmn);
502             intent.putExtra(Intents.EXTRA_PLMN, plmn);
503             phone.getContext().sendStickyBroadcast(intent);
504         }
505 
506         mCurPlmn = plmn;
507     }
508 
509     @Override
getPhone()510     protected Phone getPhone() {
511         return phone;
512     }
513 
514     /**
515     * Determine data network type based on radio technology.
516     */
setCdmaTechnology(int radioTechnology)517     protected void setCdmaTechnology(int radioTechnology){
518         mNewDataConnectionState = radioTechnologyToDataServiceState(radioTechnology);
519         newSS.setRadioTechnology(radioTechnology);
520         newNetworkType = radioTechnology;
521     }
522 
523     /**
524     * Hanlde the PollStateResult message
525     */
handlePollStateResultMessage(int what, AsyncResult ar)526     protected void handlePollStateResultMessage(int what, AsyncResult ar){
527         int ints[];
528         String states[];
529         switch (what) {
530         case EVENT_POLL_STATE_REGISTRATION_CDMA: // Handle RIL_REQUEST_REGISTRATION_STATE.
531             states = (String[])ar.result;
532 
533             int registrationState = 4;     //[0] registrationState
534             int radioTechnology = -1;      //[3] radioTechnology
535             int baseStationId = -1;        //[4] baseStationId
536             //[5] baseStationLatitude
537             int baseStationLatitude = CdmaCellLocation.INVALID_LAT_LONG;
538             //[6] baseStationLongitude
539             int baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
540             int cssIndicator = 0;          //[7] init with 0, because it is treated as a boolean
541             int systemId = 0;              //[8] systemId
542             int networkId = 0;             //[9] networkId
543             int roamingIndicator = -1;     //[10] Roaming indicator
544             int systemIsInPrl = 0;         //[11] Indicates if current system is in PRL
545             int defaultRoamingIndicator = 0;  //[12] Is default roaming indicator from PRL
546             int reasonForDenial = 0;       //[13] Denial reason if registrationState = 3
547 
548             if (states.length >= 14) {
549                 try {
550                     if (states[0] != null) {
551                         registrationState = Integer.parseInt(states[0]);
552                     }
553                     if (states[3] != null) {
554                         radioTechnology = Integer.parseInt(states[3]);
555                     }
556                     if (states[4] != null) {
557                         baseStationId = Integer.parseInt(states[4]);
558                     }
559                     if (states[5] != null) {
560                         baseStationLatitude = Integer.parseInt(states[5]);
561                     }
562                     if (states[6] != null) {
563                         baseStationLongitude = Integer.parseInt(states[6]);
564                     }
565                     // Some carriers only return lat-lngs of 0,0
566                     if (baseStationLatitude == 0 && baseStationLongitude == 0) {
567                         baseStationLatitude  = CdmaCellLocation.INVALID_LAT_LONG;
568                         baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
569                     }
570                     if (states[7] != null) {
571                         cssIndicator = Integer.parseInt(states[7]);
572                     }
573                     if (states[8] != null) {
574                         systemId = Integer.parseInt(states[8]);
575                     }
576                     if (states[9] != null) {
577                         networkId = Integer.parseInt(states[9]);
578                     }
579                     if (states[10] != null) {
580                         roamingIndicator = Integer.parseInt(states[10]);
581                     }
582                     if (states[11] != null) {
583                         systemIsInPrl = Integer.parseInt(states[11]);
584                     }
585                     if (states[12] != null) {
586                         defaultRoamingIndicator = Integer.parseInt(states[12]);
587                     }
588                     if (states[13] != null) {
589                         reasonForDenial = Integer.parseInt(states[13]);
590                     }
591                 } catch (NumberFormatException ex) {
592                     loge("EVENT_POLL_STATE_REGISTRATION_CDMA: error parsing: " + ex);
593                 }
594             } else {
595                 throw new RuntimeException("Warning! Wrong number of parameters returned from "
596                                      + "RIL_REQUEST_REGISTRATION_STATE: expected 14 or more "
597                                      + "strings and got " + states.length + " strings");
598             }
599 
600             mRegistrationState = registrationState;
601             // When registration state is roaming and TSB58
602             // roaming indicator is not in the carrier-specified
603             // list of ERIs for home system, mCdmaRoaming is true.
604             mCdmaRoaming =
605                     regCodeIsRoaming(registrationState) && !isRoamIndForHomeSystem(states[10]);
606             newSS.setState (regCodeToServiceState(registrationState));
607 
608             setCdmaTechnology(radioTechnology);
609 
610             newSS.setCssIndicator(cssIndicator);
611             newSS.setSystemAndNetworkId(systemId, networkId);
612             mRoamingIndicator = roamingIndicator;
613             mIsInPrl = (systemIsInPrl == 0) ? false : true;
614             mDefaultRoamingIndicator = defaultRoamingIndicator;
615 
616 
617             // Values are -1 if not available.
618             newCellLoc.setCellLocationData(baseStationId, baseStationLatitude,
619                     baseStationLongitude, systemId, networkId);
620 
621             if (reasonForDenial == 0) {
622                 mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_GEN;
623             } else if (reasonForDenial == 1) {
624                 mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_AUTH;
625             } else {
626                 mRegistrationDeniedReason = "";
627             }
628 
629             if (mRegistrationState == 3) {
630                 if (DBG) log("Registration denied, " + mRegistrationDeniedReason);
631             }
632             break;
633 
634         case EVENT_POLL_STATE_OPERATOR_CDMA: // Handle RIL_REQUEST_OPERATOR
635             String opNames[] = (String[])ar.result;
636 
637             if (opNames != null && opNames.length >= 3) {
638                 // If the NUMERIC field isn't valid use PROPERTY_CDMA_HOME_OPERATOR_NUMERIC
639                 if ((opNames[2] == null) || (opNames[2].length() < 5)
640                         || ("00000".equals(opNames[2]))) {
641                     opNames[2] = SystemProperties.get(
642                             CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC, "00000");
643                     if (DBG) {
644                         log("RIL_REQUEST_OPERATOR.response[2], the numeric, " +
645                                 " is bad. Using SystemProperties '" +
646                                         CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC +
647                                 "'= " + opNames[2]);
648                     }
649                 }
650                 if (cm.getNvState().isNVReady()) {
651                     // In CDMA in case on NV, the ss.mOperatorAlphaLong is set later with the
652                     // ERI text, so here it is ignored what is coming from the modem.
653                     newSS.setOperatorName(null, opNames[1], opNames[2]);
654                 } else {
655                     newSS.setOperatorName(opNames[0], opNames[1], opNames[2]);
656                 }
657             } else {
658                 if (DBG) log("EVENT_POLL_STATE_OPERATOR_CDMA: error parsing opNames");
659             }
660             break;
661         default:
662             loge("handlePollStateResultMessage: RIL response handle in wrong phone!"
663                     + " Expected CDMA RIL request and get GSM RIL request.");
664         break;
665         }
666     }
667 
668     /**
669      * Handle the result of one of the pollState() - related requests
670      */
671     @Override
handlePollStateResult(int what, AsyncResult ar)672     protected void handlePollStateResult(int what, AsyncResult ar) {
673         // Ignore stale requests from last poll.
674         if (ar.userObj != pollingContext) return;
675 
676         if (ar.exception != null) {
677             CommandException.Error err=null;
678 
679             if (ar.exception instanceof CommandException) {
680                 err = ((CommandException)(ar.exception)).getCommandError();
681             }
682 
683             if (err == CommandException.Error.RADIO_NOT_AVAILABLE) {
684                 // Radio has crashed or turned off.
685                 cancelPollState();
686                 return;
687             }
688 
689             if (!cm.getRadioState().isOn()) {
690                 // Radio has crashed or turned off.
691                 cancelPollState();
692                 return;
693             }
694 
695             if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
696                 loge("handlePollStateResult: RIL returned an error where it must succeed"
697                         + ar.exception);
698             }
699         } else try {
700             handlePollStateResultMessage(what, ar);
701         } catch (RuntimeException ex) {
702             loge("handlePollStateResult: Exception while polling service state. "
703                     + "Probably malformed RIL response." + ex);
704         }
705 
706         pollingContext[0]--;
707 
708         if (pollingContext[0] == 0) {
709             boolean namMatch = false;
710             if (!isSidsAllZeros() && isHomeSid(newSS.getSystemId())) {
711                 namMatch = true;
712             }
713 
714             // Setting SS Roaming (general)
715             if (isSubscriptionFromRuim) {
716                 newSS.setRoaming(isRoamingBetweenOperators(mCdmaRoaming, newSS));
717             } else {
718                 newSS.setRoaming(mCdmaRoaming);
719             }
720 
721             // Setting SS CdmaRoamingIndicator and CdmaDefaultRoamingIndicator
722             newSS.setCdmaDefaultRoamingIndicator(mDefaultRoamingIndicator);
723             newSS.setCdmaRoamingIndicator(mRoamingIndicator);
724             boolean isPrlLoaded = true;
725             if (TextUtils.isEmpty(mPrlVersion)) {
726                 isPrlLoaded = false;
727             }
728             if (!isPrlLoaded) {
729                 newSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF);
730             } else if (!isSidsAllZeros()) {
731                 if (!namMatch && !mIsInPrl) {
732                     // Use default
733                     newSS.setCdmaRoamingIndicator(mDefaultRoamingIndicator);
734                 } else if (namMatch && !mIsInPrl) {
735                     newSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_FLASH);
736                 } else if (!namMatch && mIsInPrl) {
737                     // Use the one from PRL/ERI
738                     newSS.setCdmaRoamingIndicator(mRoamingIndicator);
739                 } else {
740                     // It means namMatch && mIsInPrl
741                     if ((mRoamingIndicator <= 2)) {
742                         newSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF);
743                     } else {
744                         // Use the one from PRL/ERI
745                         newSS.setCdmaRoamingIndicator(mRoamingIndicator);
746                     }
747                 }
748             }
749 
750             int roamingIndicator = newSS.getCdmaRoamingIndicator();
751             newSS.setCdmaEriIconIndex(phone.mEriManager.getCdmaEriIconIndex(roamingIndicator,
752                     mDefaultRoamingIndicator));
753             newSS.setCdmaEriIconMode(phone.mEriManager.getCdmaEriIconMode(roamingIndicator,
754                     mDefaultRoamingIndicator));
755 
756             // NOTE: Some operator may require overriding mCdmaRoaming
757             // (set by the modem), depending on the mRoamingIndicator.
758 
759             if (DBG) {
760                 log("Set CDMA Roaming Indicator to: " + newSS.getCdmaRoamingIndicator()
761                     + ". mCdmaRoaming = " + mCdmaRoaming + ", isPrlLoaded = " + isPrlLoaded
762                     + ". namMatch = " + namMatch + " , mIsInPrl = " + mIsInPrl
763                     + ", mRoamingIndicator = " + mRoamingIndicator
764                     + ", mDefaultRoamingIndicator= " + mDefaultRoamingIndicator);
765             }
766             pollStateDone();
767         }
768 
769     }
770 
setSignalStrengthDefaultValues()771     protected void setSignalStrengthDefaultValues() {
772         mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, false);
773     }
774 
775     /**
776      * A complete "service state" from our perspective is
777      * composed of a handful of separate requests to the radio.
778      *
779      * We make all of these requests at once, but then abandon them
780      * and start over again if the radio notifies us that some
781      * event has changed
782      */
783     protected void
pollState()784     pollState() {
785         pollingContext = new int[1];
786         pollingContext[0] = 0;
787 
788         switch (cm.getRadioState()) {
789         case RADIO_UNAVAILABLE:
790             newSS.setStateOutOfService();
791             newCellLoc.setStateInvalid();
792             setSignalStrengthDefaultValues();
793             mGotCountryCode = false;
794 
795             pollStateDone();
796             break;
797 
798         case RADIO_OFF:
799             newSS.setStateOff();
800             newCellLoc.setStateInvalid();
801             setSignalStrengthDefaultValues();
802             mGotCountryCode = false;
803 
804             pollStateDone();
805             break;
806 
807         case SIM_NOT_READY:
808         case SIM_LOCKED_OR_ABSENT:
809         case SIM_READY:
810             if (DBG) log("Radio Technology Change ongoing, setting SS to off");
811             newSS.setStateOff();
812             newCellLoc.setStateInvalid();
813             setSignalStrengthDefaultValues();
814             mGotCountryCode = false;
815 
816             // NOTE: pollStateDone() is not needed in this case
817             break;
818 
819         default:
820             // Issue all poll-related commands at once, then count
821             // down the responses which are allowed to arrive
822             // out-of-order.
823 
824             pollingContext[0]++;
825             // RIL_REQUEST_OPERATOR is necessary for CDMA
826             cm.getOperator(
827                     obtainMessage(EVENT_POLL_STATE_OPERATOR_CDMA, pollingContext));
828 
829             pollingContext[0]++;
830             // RIL_REQUEST_VOICE_REGISTRATION_STATE is necessary for CDMA
831             cm.getVoiceRegistrationState(
832                     obtainMessage(EVENT_POLL_STATE_REGISTRATION_CDMA, pollingContext));
833 
834             break;
835         }
836     }
837 
fixTimeZone(String isoCountryCode)838     protected void fixTimeZone(String isoCountryCode) {
839         TimeZone zone = null;
840         // If the offset is (0, false) and the time zone property
841         // is set, use the time zone property rather than GMT.
842         String zoneName = SystemProperties.get(TIMEZONE_PROPERTY);
843         if ((mZoneOffset == 0) && (mZoneDst == false) && (zoneName != null)
844                 && (zoneName.length() > 0)
845                 && (Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode) < 0)) {
846             // For NITZ string without time zone,
847             // need adjust time to reflect default time zone setting
848             zone = TimeZone.getDefault();
849             long tzOffset;
850             tzOffset = zone.getOffset(System.currentTimeMillis());
851             if (getAutoTime()) {
852                 setAndBroadcastNetworkSetTime(System.currentTimeMillis() - tzOffset);
853             } else {
854                 // Adjust the saved NITZ time to account for tzOffset.
855                 mSavedTime = mSavedTime - tzOffset;
856             }
857         } else if (isoCountryCode.equals("")) {
858             // Country code not found. This is likely a test network.
859             // Get a TimeZone based only on the NITZ parameters (best guess).
860             zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime);
861         } else {
862             zone = TimeUtils.getTimeZone(mZoneOffset, mZoneDst, mZoneTime, isoCountryCode);
863         }
864 
865         mNeedFixZone = false;
866 
867         if (zone != null) {
868             if (getAutoTimeZone()) {
869                 setAndBroadcastNetworkSetTimeZone(zone.getID());
870             }
871             saveNitzTimeZone(zone.getID());
872         }
873     }
874 
pollStateDone()875     protected void pollStateDone() {
876         if (DBG) log("pollStateDone: oldSS=[" + ss + "] newSS=[" + newSS + "]");
877 
878         boolean hasRegistered =
879             ss.getState() != ServiceState.STATE_IN_SERVICE
880             && newSS.getState() == ServiceState.STATE_IN_SERVICE;
881 
882         boolean hasDeregistered =
883             ss.getState() == ServiceState.STATE_IN_SERVICE
884             && newSS.getState() != ServiceState.STATE_IN_SERVICE;
885 
886         boolean hasCdmaDataConnectionAttached =
887             mDataConnectionState != ServiceState.STATE_IN_SERVICE
888             && mNewDataConnectionState == ServiceState.STATE_IN_SERVICE;
889 
890         boolean hasCdmaDataConnectionDetached =
891             mDataConnectionState == ServiceState.STATE_IN_SERVICE
892             && mNewDataConnectionState != ServiceState.STATE_IN_SERVICE;
893 
894         boolean hasCdmaDataConnectionChanged =
895                        mDataConnectionState != mNewDataConnectionState;
896 
897         boolean hasNetworkTypeChanged = networkType != newNetworkType;
898 
899         boolean hasChanged = !newSS.equals(ss);
900 
901         boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming();
902 
903         boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming();
904 
905         boolean hasLocationChanged = !newCellLoc.equals(cellLoc);
906 
907         // Add an event log when connection state changes
908         if (ss.getState() != newSS.getState() ||
909                 mDataConnectionState != mNewDataConnectionState) {
910             EventLog.writeEvent(EventLogTags.CDMA_SERVICE_STATE_CHANGE,
911                     ss.getState(), mDataConnectionState,
912                     newSS.getState(), mNewDataConnectionState);
913         }
914 
915         ServiceState tss;
916         tss = ss;
917         ss = newSS;
918         newSS = tss;
919         // clean slate for next time
920         newSS.setStateOutOfService();
921 
922         CdmaCellLocation tcl = cellLoc;
923         cellLoc = newCellLoc;
924         newCellLoc = tcl;
925 
926         mDataConnectionState = mNewDataConnectionState;
927         networkType = newNetworkType;
928         // this new state has been applied - forget it until we get a new new state
929         newNetworkType = 0;
930 
931         newSS.setStateOutOfService(); // clean slate for next time
932 
933         if (hasNetworkTypeChanged) {
934             phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
935                     ServiceState.radioTechnologyToString(networkType));
936         }
937 
938         if (hasRegistered) {
939             mNetworkAttachedRegistrants.notifyRegistrants();
940         }
941 
942         if (hasChanged) {
943             if (cm.getRadioState().isNVReady()) {
944                 String eriText;
945                 // Now the CDMAPhone sees the new ServiceState so it can get the new ERI text
946                 if (ss.getState() == ServiceState.STATE_IN_SERVICE) {
947                     eriText = phone.getCdmaEriText();
948                 } else {
949                     // Note that ServiceState.STATE_OUT_OF_SERVICE is valid used for
950                     // mRegistrationState 0,2,3 and 4
951                     eriText = phone.getContext().getText(
952                             com.android.internal.R.string.roamingTextSearching).toString();
953                 }
954                 ss.setOperatorAlphaLong(eriText);
955             }
956 
957             String operatorNumeric;
958 
959             phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA,
960                     ss.getOperatorAlphaLong());
961 
962             operatorNumeric = ss.getOperatorNumeric();
963             phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric);
964 
965             if (operatorNumeric == null) {
966                 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
967                 mGotCountryCode = false;
968             } else {
969                 String isoCountryCode = "";
970                 try{
971                     isoCountryCode = MccTable.countryCodeForMcc(Integer.parseInt(
972                             operatorNumeric.substring(0,3)));
973                 } catch ( NumberFormatException ex){
974                     loge("pollStateDone: countryCodeForMcc error" + ex);
975                 } catch ( StringIndexOutOfBoundsException ex) {
976                     loge("pollStateDone: countryCodeForMcc error" + ex);
977                 }
978 
979                 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY,
980                         isoCountryCode);
981                 mGotCountryCode = true;
982                 if (mNeedFixZone) {
983                     fixTimeZone(isoCountryCode);
984                 }
985             }
986 
987             phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
988                     ss.getRoaming() ? "true" : "false");
989 
990             updateSpnDisplay();
991             phone.notifyServiceStateChanged(ss);
992         }
993 
994         if (hasCdmaDataConnectionAttached) {
995             mAttachedRegistrants.notifyRegistrants();
996         }
997 
998         if (hasCdmaDataConnectionDetached) {
999             mDetachedRegistrants.notifyRegistrants();
1000         }
1001 
1002         if (hasCdmaDataConnectionChanged || hasNetworkTypeChanged) {
1003             phone.notifyDataConnection(null);
1004         }
1005 
1006         if (hasRoamingOn) {
1007             mRoamingOnRegistrants.notifyRegistrants();
1008         }
1009 
1010         if (hasRoamingOff) {
1011             mRoamingOffRegistrants.notifyRegistrants();
1012         }
1013 
1014         if (hasLocationChanged) {
1015             phone.notifyLocationChanged();
1016         }
1017     }
1018 
1019     /**
1020      * Returns a TimeZone object based only on parameters from the NITZ string.
1021      */
getNitzTimeZone(int offset, boolean dst, long when)1022     private TimeZone getNitzTimeZone(int offset, boolean dst, long when) {
1023         TimeZone guess = findTimeZone(offset, dst, when);
1024         if (guess == null) {
1025             // Couldn't find a proper timezone.  Perhaps the DST data is wrong.
1026             guess = findTimeZone(offset, !dst, when);
1027         }
1028         if (DBG) log("getNitzTimeZone returning " + (guess == null ? guess : guess.getID()));
1029         return guess;
1030     }
1031 
findTimeZone(int offset, boolean dst, long when)1032     private TimeZone findTimeZone(int offset, boolean dst, long when) {
1033         int rawOffset = offset;
1034         if (dst) {
1035             rawOffset -= 3600000;
1036         }
1037         String[] zones = TimeZone.getAvailableIDs(rawOffset);
1038         TimeZone guess = null;
1039         Date d = new Date(when);
1040         for (String zone : zones) {
1041             TimeZone tz = TimeZone.getTimeZone(zone);
1042             if (tz.getOffset(when) == offset &&
1043                     tz.inDaylightTime(d) == dst) {
1044                 guess = tz;
1045                 break;
1046             }
1047         }
1048 
1049         return guess;
1050     }
1051 
1052     /**
1053      * TODO: This code is exactly the same as in GsmServiceStateTracker
1054      * and has a TODO to not poll signal strength if screen is off.
1055      * This code should probably be hoisted to the base class so
1056      * the fix, when added, works for both.
1057      */
1058     protected void
queueNextSignalStrengthPoll()1059     queueNextSignalStrengthPoll() {
1060         if (dontPollSignalStrength || (cm.getRadioState().isGsm())) {
1061             // The radio is telling us about signal strength changes
1062             // we don't have to ask it
1063             return;
1064         }
1065 
1066         Message msg;
1067 
1068         msg = obtainMessage();
1069         msg.what = EVENT_POLL_SIGNAL_STRENGTH;
1070 
1071         // TODO Don't poll signal strength if screen is off
1072         sendMessageDelayed(msg, POLL_PERIOD_MILLIS);
1073     }
1074 
1075     /**
1076      *  send signal-strength-changed notification if changed
1077      *  Called both for solicited and unsolicited signal strength updates
1078      */
1079     protected void
onSignalStrengthResult(AsyncResult ar)1080     onSignalStrengthResult(AsyncResult ar) {
1081         SignalStrength oldSignalStrength = mSignalStrength;
1082 
1083         if (ar.exception != null) {
1084             // Most likely radio is resetting/disconnected change to default values.
1085             setSignalStrengthDefaultValues();
1086         } else {
1087             int[] ints = (int[])ar.result;
1088             int offset = 2;
1089             int cdmaDbm = (ints[offset] > 0) ? -ints[offset] : -120;
1090             int cdmaEcio = (ints[offset+1] > 0) ? -ints[offset+1] : -160;
1091             int evdoRssi = (ints[offset+2] > 0) ? -ints[offset+2] : -120;
1092             int evdoEcio = (ints[offset+3] > 0) ? -ints[offset+3] : -1;
1093             int evdoSnr  = ((ints[offset+4] > 0) && (ints[offset+4] <= 8)) ? ints[offset+4] : -1;
1094 
1095             //log(String.format("onSignalStrengthResult cdmaDbm=%d cdmaEcio=%d evdoRssi=%d evdoEcio=%d evdoSnr=%d",
1096             //        cdmaDbm, cdmaEcio, evdoRssi, evdoEcio, evdoSnr));
1097             mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio,
1098                     evdoRssi, evdoEcio, evdoSnr, false);
1099         }
1100 
1101         try {
1102             phone.notifySignalStrength();
1103         } catch (NullPointerException ex) {
1104             loge("onSignalStrengthResult() Phone already destroyed: " + ex
1105                     + "SignalStrength not notified");
1106         }
1107     }
1108 
1109 
radioTechnologyToDataServiceState(int code)1110     protected int radioTechnologyToDataServiceState(int code) {
1111         int retVal = ServiceState.STATE_OUT_OF_SERVICE;
1112         switch(code) {
1113         case 0:
1114         case 1:
1115         case 2:
1116         case 3:
1117         case 4:
1118         case 5:
1119             break;
1120         case 6: // RADIO_TECHNOLOGY_1xRTT
1121         case 7: // RADIO_TECHNOLOGY_EVDO_0
1122         case 8: // RADIO_TECHNOLOGY_EVDO_A
1123         case 12: // RADIO_TECHNOLOGY_EVDO_B
1124         case 13: // RADIO_TECHNOLOGY_EHRPD
1125             retVal = ServiceState.STATE_IN_SERVICE;
1126             break;
1127         default:
1128             loge("radioTechnologyToDataServiceState: Wrong radioTechnology code.");
1129         break;
1130         }
1131         return(retVal);
1132     }
1133 
1134     /** code is registration state 0-5 from TS 27.007 7.2 */
1135     protected int
regCodeToServiceState(int code)1136     regCodeToServiceState(int code) {
1137         switch (code) {
1138         case 0: // Not searching and not registered
1139             return ServiceState.STATE_OUT_OF_SERVICE;
1140         case 1:
1141             return ServiceState.STATE_IN_SERVICE;
1142         case 2: // 2 is "searching", fall through
1143         case 3: // 3 is "registration denied", fall through
1144         case 4: // 4 is "unknown", not valid in current baseband
1145             return ServiceState.STATE_OUT_OF_SERVICE;
1146         case 5:// 5 is "Registered, roaming"
1147             return ServiceState.STATE_IN_SERVICE;
1148 
1149         default:
1150             loge("regCodeToServiceState: unexpected service state " + code);
1151         return ServiceState.STATE_OUT_OF_SERVICE;
1152         }
1153     }
1154 
getCurrentDataConnectionState()1155     public int getCurrentDataConnectionState() {
1156         return mDataConnectionState;
1157     }
1158 
1159     /**
1160      * code is registration state 0-5 from TS 27.007 7.2
1161      * returns true if registered roam, false otherwise
1162      */
1163     private boolean
regCodeIsRoaming(int code)1164     regCodeIsRoaming (int code) {
1165         // 5 is  "in service -- roam"
1166         return 5 == code;
1167     }
1168 
1169     /**
1170      * Determine whether a roaming indicator is in the carrier-specified list of ERIs for
1171      * home system
1172      *
1173      * @param roamInd roaming indicator in String
1174      * @return true if the roamInd is in the carrier-specified list of ERIs for home network
1175      */
isRoamIndForHomeSystem(String roamInd)1176     private boolean isRoamIndForHomeSystem(String roamInd) {
1177         // retrieve the carrier-specified list of ERIs for home system
1178         String homeRoamIndicators = SystemProperties.get("ro.cdma.homesystem");
1179 
1180         if (!TextUtils.isEmpty(homeRoamIndicators)) {
1181             // searches through the comma-separated list for a match,
1182             // return true if one is found.
1183             for (String homeRoamInd : homeRoamIndicators.split(",")) {
1184                 if (homeRoamInd.equals(roamInd)) {
1185                     return true;
1186                 }
1187             }
1188             // no matches found against the list!
1189             return false;
1190         }
1191 
1192         // no system property found for the roaming indicators for home system
1193         return false;
1194     }
1195 
1196     /**
1197      * Set roaming state when cdmaRoaming is true and ons is different from spn
1198      * @param cdmaRoaming TS 27.007 7.2 CREG registered roaming
1199      * @param s ServiceState hold current ons
1200      * @return true for roaming state set
1201      */
1202     private
isRoamingBetweenOperators(boolean cdmaRoaming, ServiceState s)1203     boolean isRoamingBetweenOperators(boolean cdmaRoaming, ServiceState s) {
1204         String spn = SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "empty");
1205 
1206         // NOTE: in case of RUIM we should completely ignore the ERI data file and
1207         // mOperatorAlphaLong is set from RIL_REQUEST_OPERATOR response 0 (alpha ONS)
1208         String onsl = s.getOperatorAlphaLong();
1209         String onss = s.getOperatorAlphaShort();
1210 
1211         boolean equalsOnsl = onsl != null && spn.equals(onsl);
1212         boolean equalsOnss = onss != null && spn.equals(onss);
1213 
1214         return cdmaRoaming && !(equalsOnsl || equalsOnss);
1215     }
1216 
1217 
1218     /**
1219      * nitzReceiveTime is time_t that the NITZ time was posted
1220      */
1221 
1222     private
setTimeFromNITZString(String nitz, long nitzReceiveTime)1223     void setTimeFromNITZString (String nitz, long nitzReceiveTime)
1224     {
1225         // "yy/mm/dd,hh:mm:ss(+/-)tz"
1226         // tz is in number of quarter-hours
1227 
1228         long start = SystemClock.elapsedRealtime();
1229         if (DBG) {
1230             log("NITZ: " + nitz + "," + nitzReceiveTime +
1231                         " start=" + start + " delay=" + (start - nitzReceiveTime));
1232         }
1233 
1234         try {
1235             /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
1236              * offset as well (which we won't worry about until later) */
1237             Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
1238 
1239             c.clear();
1240             c.set(Calendar.DST_OFFSET, 0);
1241 
1242             String[] nitzSubs = nitz.split("[/:,+-]");
1243 
1244             int year = 2000 + Integer.parseInt(nitzSubs[0]);
1245             c.set(Calendar.YEAR, year);
1246 
1247             // month is 0 based!
1248             int month = Integer.parseInt(nitzSubs[1]) - 1;
1249             c.set(Calendar.MONTH, month);
1250 
1251             int date = Integer.parseInt(nitzSubs[2]);
1252             c.set(Calendar.DATE, date);
1253 
1254             int hour = Integer.parseInt(nitzSubs[3]);
1255             c.set(Calendar.HOUR, hour);
1256 
1257             int minute = Integer.parseInt(nitzSubs[4]);
1258             c.set(Calendar.MINUTE, minute);
1259 
1260             int second = Integer.parseInt(nitzSubs[5]);
1261             c.set(Calendar.SECOND, second);
1262 
1263             boolean sign = (nitz.indexOf('-') == -1);
1264 
1265             int tzOffset = Integer.parseInt(nitzSubs[6]);
1266 
1267             int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7])
1268                                               : 0;
1269 
1270             // The zone offset received from NITZ is for current local time,
1271             // so DST correction is already applied.  Don't add it again.
1272             //
1273             // tzOffset += dst * 4;
1274             //
1275             // We could unapply it if we wanted the raw offset.
1276 
1277             tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000;
1278 
1279             TimeZone    zone = null;
1280 
1281             // As a special extension, the Android emulator appends the name of
1282             // the host computer's timezone to the nitz string. this is zoneinfo
1283             // timezone name of the form Area!Location or Area!Location!SubLocation
1284             // so we need to convert the ! into /
1285             if (nitzSubs.length >= 9) {
1286                 String  tzname = nitzSubs[8].replace('!','/');
1287                 zone = TimeZone.getTimeZone( tzname );
1288             }
1289 
1290             String iso = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY);
1291 
1292             if (zone == null) {
1293 
1294                 if (mGotCountryCode) {
1295                     if (iso != null && iso.length() > 0) {
1296                         zone = TimeUtils.getTimeZone(tzOffset, dst != 0,
1297                                 c.getTimeInMillis(),
1298                                 iso);
1299                     } else {
1300                         // We don't have a valid iso country code.  This is
1301                         // most likely because we're on a test network that's
1302                         // using a bogus MCC (eg, "001"), so get a TimeZone
1303                         // based only on the NITZ parameters.
1304                         zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis());
1305                     }
1306                 }
1307             }
1308 
1309             if (zone == null) {
1310                 // We got the time before the country, so we don't know
1311                 // how to identify the DST rules yet.  Save the information
1312                 // and hope to fix it up later.
1313 
1314                 mNeedFixZone = true;
1315                 mZoneOffset  = tzOffset;
1316                 mZoneDst     = dst != 0;
1317                 mZoneTime    = c.getTimeInMillis();
1318             }
1319 
1320             if (zone != null) {
1321                 if (getAutoTimeZone()) {
1322                     setAndBroadcastNetworkSetTimeZone(zone.getID());
1323                 }
1324                 saveNitzTimeZone(zone.getID());
1325             }
1326 
1327             String ignore = SystemProperties.get("gsm.ignore-nitz");
1328             if (ignore != null && ignore.equals("yes")) {
1329                 if (DBG) log("NITZ: Not setting clock because gsm.ignore-nitz is set");
1330                 return;
1331             }
1332 
1333             try {
1334                 mWakeLock.acquire();
1335 
1336                 /**
1337                  * Correct the NITZ time by how long its taken to get here.
1338                  */
1339                 long millisSinceNitzReceived
1340                         = SystemClock.elapsedRealtime() - nitzReceiveTime;
1341 
1342                 if (millisSinceNitzReceived < 0) {
1343                     // Sanity check: something is wrong
1344                     if (DBG) {
1345                         log("NITZ: not setting time, clock has rolled "
1346                                         + "backwards since NITZ time was received, "
1347                                         + nitz);
1348                     }
1349                     return;
1350                 }
1351 
1352                 if (millisSinceNitzReceived > Integer.MAX_VALUE) {
1353                     // If the time is this far off, something is wrong > 24 days!
1354                     if (DBG) {
1355                         log("NITZ: not setting time, processing has taken "
1356                                     + (millisSinceNitzReceived / (1000 * 60 * 60 * 24))
1357                                     + " days");
1358                     }
1359                     return;
1360                 }
1361 
1362                 // Note: with range checks above, cast to int is safe
1363                 c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived);
1364 
1365                 if (getAutoTime()) {
1366                     /**
1367                      * Update system time automatically
1368                      */
1369                     long gained = c.getTimeInMillis() - System.currentTimeMillis();
1370                     long timeSinceLastUpdate = SystemClock.elapsedRealtime() - mSavedAtTime;
1371                     int nitzUpdateSpacing = Settings.Secure.getInt(cr,
1372                             Settings.Secure.NITZ_UPDATE_SPACING, mNitzUpdateSpacing);
1373                     int nitzUpdateDiff = Settings.Secure.getInt(cr,
1374                             Settings.Secure.NITZ_UPDATE_DIFF, mNitzUpdateDiff);
1375 
1376                     if ((mSavedAtTime == 0) || (timeSinceLastUpdate > nitzUpdateSpacing)
1377                             || (Math.abs(gained) > nitzUpdateDiff)) {
1378                         if (DBG) {
1379                             log("NITZ: Auto updating time of day to " + c.getTime()
1380                                 + " NITZ receive delay=" + millisSinceNitzReceived
1381                                 + "ms gained=" + gained + "ms from " + nitz);
1382                         }
1383 
1384                         setAndBroadcastNetworkSetTime(c.getTimeInMillis());
1385                     } else {
1386                         if (DBG) {
1387                             log("NITZ: ignore, a previous update was "
1388                                 + timeSinceLastUpdate + "ms ago and gained=" + gained + "ms");
1389                         }
1390                         return;
1391                     }
1392                 }
1393 
1394                 /**
1395                  * Update properties and save the time we did the update
1396                  */
1397                 if (DBG) log("NITZ: update nitz time property");
1398                 SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis()));
1399                 mSavedTime = c.getTimeInMillis();
1400                 mSavedAtTime = SystemClock.elapsedRealtime();
1401             } finally {
1402                 long end = SystemClock.elapsedRealtime();
1403                 if (DBG) log("NITZ: end=" + end + " dur=" + (end - start));
1404                 mWakeLock.release();
1405             }
1406         } catch (RuntimeException ex) {
1407             loge("NITZ: Parsing NITZ time " + nitz + " ex=" + ex);
1408         }
1409     }
1410 
getAutoTime()1411     private boolean getAutoTime() {
1412         try {
1413             return Settings.System.getInt(cr, Settings.System.AUTO_TIME) > 0;
1414         } catch (SettingNotFoundException snfe) {
1415             return true;
1416         }
1417     }
1418 
getAutoTimeZone()1419     private boolean getAutoTimeZone() {
1420         try {
1421             return Settings.System.getInt(cr, Settings.System.AUTO_TIME_ZONE) > 0;
1422         } catch (SettingNotFoundException snfe) {
1423             return true;
1424         }
1425     }
1426 
saveNitzTimeZone(String zoneId)1427     private void saveNitzTimeZone(String zoneId) {
1428         mSavedTimeZone = zoneId;
1429     }
1430 
1431     /**
1432      * Set the timezone and send out a sticky broadcast so the system can
1433      * determine if the timezone was set by the carrier.
1434      *
1435      * @param zoneId timezone set by carrier
1436      */
setAndBroadcastNetworkSetTimeZone(String zoneId)1437     private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
1438         AlarmManager alarm =
1439             (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE);
1440         alarm.setTimeZone(zoneId);
1441         Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
1442         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1443         intent.putExtra("time-zone", zoneId);
1444         phone.getContext().sendStickyBroadcast(intent);
1445     }
1446 
1447     /**
1448      * Set the time and Send out a sticky broadcast so the system can determine
1449      * if the time was set by the carrier.
1450      *
1451      * @param time time set by network
1452      */
setAndBroadcastNetworkSetTime(long time)1453     private void setAndBroadcastNetworkSetTime(long time) {
1454         SystemClock.setCurrentTimeMillis(time);
1455         Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
1456         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1457         intent.putExtra("time", time);
1458         phone.getContext().sendStickyBroadcast(intent);
1459     }
1460 
revertToNitzTime()1461     private void revertToNitzTime() {
1462         if (Settings.System.getInt(cr, Settings.System.AUTO_TIME, 0) == 0) {
1463             return;
1464         }
1465         if (DBG) {
1466             log("revertToNitzTime: mSavedTime=" + mSavedTime + " mSavedAtTime=" + mSavedAtTime);
1467         }
1468         if (mSavedTime != 0 && mSavedAtTime != 0) {
1469             setAndBroadcastNetworkSetTime(mSavedTime
1470                     + (SystemClock.elapsedRealtime() - mSavedAtTime));
1471         }
1472     }
1473 
revertToNitzTimeZone()1474     private void revertToNitzTimeZone() {
1475         if (Settings.System.getInt(phone.getContext().getContentResolver(),
1476                 Settings.System.AUTO_TIME_ZONE, 0) == 0) {
1477             return;
1478         }
1479         if (DBG) log("revertToNitzTimeZone: tz='" + mSavedTimeZone);
1480         if (mSavedTimeZone != null) {
1481             setAndBroadcastNetworkSetTimeZone(mSavedTimeZone);
1482         }
1483     }
1484 
isSidsAllZeros()1485     protected boolean isSidsAllZeros() {
1486         if (mHomeSystemId != null) {
1487             for (int i=0; i < mHomeSystemId.length; i++) {
1488                 if (mHomeSystemId[i] != 0) {
1489                     return false;
1490                 }
1491             }
1492         }
1493         return true;
1494     }
1495 
1496     /**
1497      * Check whether a specified system ID that matches one of the home system IDs.
1498      */
isHomeSid(int sid)1499     private boolean isHomeSid(int sid) {
1500         if (mHomeSystemId != null) {
1501             for (int i=0; i < mHomeSystemId.length; i++) {
1502                 if (sid == mHomeSystemId[i]) {
1503                     return true;
1504                 }
1505             }
1506         }
1507         return false;
1508     }
1509 
1510     /**
1511      * @return true if phone is camping on a technology
1512      * that could support voice and data simultaneously.
1513      */
isConcurrentVoiceAndDataAllowed()1514     public boolean isConcurrentVoiceAndDataAllowed() {
1515         // Note: it needs to be confirmed which CDMA network types
1516         // can support voice and data calls concurrently.
1517         // For the time-being, the return value will be false.
1518         return false;
1519     }
1520 
getMdnNumber()1521     public String getMdnNumber() {
1522         return mMdn;
1523     }
1524 
getCdmaMin()1525     public String getCdmaMin() {
1526          return mMin;
1527     }
1528 
1529     /** Returns null if NV is not yet ready */
getPrlVersion()1530     public String getPrlVersion() {
1531         return mPrlVersion;
1532     }
1533 
1534     /**
1535      * Returns IMSI as MCC + MNC + MIN
1536      */
getImsi()1537     String getImsi() {
1538         // TODO: When RUIM is enabled, IMSI will come from RUIM not build-time props.
1539         String operatorNumeric = SystemProperties.get(
1540                 TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
1541 
1542         if (!TextUtils.isEmpty(operatorNumeric) && getCdmaMin() != null) {
1543             return (operatorNumeric + getCdmaMin());
1544         } else {
1545             return null;
1546         }
1547     }
1548 
1549     /**
1550      * Check if subscription data has been assigned to mMin
1551      *
1552      * return true if MIN info is ready; false otherwise.
1553      */
isMinInfoReady()1554     public boolean isMinInfoReady() {
1555         return mIsMinInfoReady;
1556     }
1557 
1558     /**
1559      * Returns OTASP_UNKNOWN, OTASP_NEEDED or OTASP_NOT_NEEDED
1560      */
getOtasp()1561     int getOtasp() {
1562         int provisioningState;
1563         if (mMin == null || (mMin.length() < 6)) {
1564             if (DBG) log("getOtasp: bad mMin='" + mMin + "'");
1565             provisioningState = OTASP_UNKNOWN;
1566         } else {
1567             if ((mMin.equals(UNACTIVATED_MIN_VALUE)
1568                     || mMin.substring(0,6).equals(UNACTIVATED_MIN2_VALUE))
1569                     || SystemProperties.getBoolean("test_cdma_setup", false)) {
1570                 provisioningState = OTASP_NEEDED;
1571             } else {
1572                 provisioningState = OTASP_NOT_NEEDED;
1573             }
1574         }
1575         if (DBG) log("getOtasp: state=" + provisioningState);
1576         return provisioningState;
1577     }
1578 
1579     @Override
hangupAndPowerOff()1580     protected void hangupAndPowerOff() {
1581         // hang up all active voice calls
1582         phone.mCT.ringingCall.hangupIfAlive();
1583         phone.mCT.backgroundCall.hangupIfAlive();
1584         phone.mCT.foregroundCall.hangupIfAlive();
1585         cm.setRadioPower(false, null);
1586     }
1587 
parseSidNid(String sidStr, String nidStr)1588     protected void parseSidNid (String sidStr, String nidStr) {
1589         if (sidStr != null) {
1590             String[] sid = sidStr.split(",");
1591             mHomeSystemId = new int[sid.length];
1592             for (int i = 0; i < sid.length; i++) {
1593                 try {
1594                     mHomeSystemId[i] = Integer.parseInt(sid[i]);
1595                 } catch (NumberFormatException ex) {
1596                     loge("error parsing system id: " + ex);
1597                 }
1598             }
1599         }
1600         if (DBG) log("CDMA_SUBSCRIPTION: SID=" + sidStr);
1601 
1602         if (nidStr != null) {
1603             String[] nid = nidStr.split(",");
1604             mHomeNetworkId = new int[nid.length];
1605             for (int i = 0; i < nid.length; i++) {
1606                 try {
1607                     mHomeNetworkId[i] = Integer.parseInt(nid[i]);
1608                 } catch (NumberFormatException ex) {
1609                     loge("CDMA_SUBSCRIPTION: error parsing network id: " + ex);
1610                 }
1611             }
1612         }
1613         if (DBG) log("CDMA_SUBSCRIPTION: NID=" + nidStr);
1614     }
1615 
updateOtaspState()1616     protected void updateOtaspState() {
1617         int otaspMode = getOtasp();
1618         int oldOtaspMode = mCurrentOtaspMode;
1619         mCurrentOtaspMode = otaspMode;
1620 
1621         // Notify apps subscription info is ready
1622         if (cdmaForSubscriptionInfoReadyRegistrants != null) {
1623             if (DBG) log("CDMA_SUBSCRIPTION: call notifyRegistrants()");
1624             cdmaForSubscriptionInfoReadyRegistrants.notifyRegistrants();
1625         }
1626         if (oldOtaspMode != mCurrentOtaspMode) {
1627             if (DBG) {
1628                 log("CDMA_SUBSCRIPTION: call notifyOtaspChanged old otaspMode=" +
1629                     oldOtaspMode + " new otaspMode=" + mCurrentOtaspMode);
1630             }
1631             phone.notifyOtaspChanged(mCurrentOtaspMode);
1632         }
1633     }
1634 
1635     @Override
log(String s)1636     protected void log(String s) {
1637         Log.d(LOG_TAG, "[CdmaSST] " + s);
1638     }
1639 
1640     @Override
loge(String s)1641     protected void loge(String s) {
1642         Log.e(LOG_TAG, "[CdmaSST] " + s);
1643     }
1644 }
1645