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