Home | History | Annotate | Download | only in gsm
      1 /*
      2  * Copyright (C) 2006 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.gsm;
     18 
     19 import android.app.AlarmManager;
     20 import android.app.Notification;
     21 import android.app.NotificationManager;
     22 import android.app.PendingIntent;
     23 import android.content.BroadcastReceiver;
     24 import android.content.ContentResolver;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.IntentFilter;
     28 import android.content.res.Resources;
     29 import android.database.ContentObserver;
     30 import android.os.AsyncResult;
     31 import android.os.Handler;
     32 import android.os.Message;
     33 import android.os.PowerManager;
     34 import android.os.Registrant;
     35 import android.os.RegistrantList;
     36 import android.os.SystemClock;
     37 import android.os.SystemProperties;
     38 import android.provider.Settings;
     39 import android.provider.Settings.SettingNotFoundException;
     40 import android.provider.Telephony.Intents;
     41 import android.telephony.ServiceState;
     42 import android.telephony.SignalStrength;
     43 import android.telephony.TelephonyManager;
     44 import android.telephony.gsm.GsmCellLocation;
     45 import android.text.TextUtils;
     46 import android.util.Config;
     47 import android.util.EventLog;
     48 import android.util.Log;
     49 import android.util.TimeUtils;
     50 
     51 import com.android.internal.telephony.CommandException;
     52 import com.android.internal.telephony.CommandsInterface;
     53 import com.android.internal.telephony.DataConnectionTracker;
     54 import com.android.internal.telephony.EventLogTags;
     55 import com.android.internal.telephony.IccCard;
     56 import com.android.internal.telephony.MccTable;
     57 import com.android.internal.telephony.RILConstants;
     58 import com.android.internal.telephony.ServiceStateTracker;
     59 import com.android.internal.telephony.TelephonyIntents;
     60 import com.android.internal.telephony.TelephonyProperties;
     61 
     62 import java.util.Arrays;
     63 import java.util.Calendar;
     64 import java.util.Date;
     65 import java.util.TimeZone;
     66 
     67 /**
     68  * {@hide}
     69  */
     70 final class GsmServiceStateTracker extends ServiceStateTracker {
     71     static final String LOG_TAG = "GSM";
     72     static final boolean DBG = true;
     73 
     74     GSMPhone phone;
     75     GsmCellLocation cellLoc;
     76     GsmCellLocation newCellLoc;
     77     int mPreferredNetworkType;
     78     RestrictedState rs;
     79 
     80     private int gprsState = ServiceState.STATE_OUT_OF_SERVICE;
     81     private int newGPRSState = ServiceState.STATE_OUT_OF_SERVICE;
     82 
     83     /**
     84      *  Values correspond to ServiceStateTracker.DATA_ACCESS_ definitions.
     85      */
     86     private int networkType = 0;
     87     private int newNetworkType = 0;
     88 
     89     /**
     90      * GSM roaming status solely based on TS 27.007 7.2 CREG. Only used by
     91      * handlePollStateResult to store CREG roaming result.
     92      */
     93     private boolean mGsmRoaming = false;
     94 
     95     /**
     96      * Data roaming status solely based on TS 27.007 10.1.19 CGREG. Only used by
     97      * handlePollStateResult to store CGREG roaming result.
     98      */
     99     private boolean mDataRoaming = false;
    100 
    101     /**
    102      * Mark when service state is in emergency call only mode
    103      */
    104     private boolean mEmergencyOnly = false;
    105 
    106     private RegistrantList gprsAttachedRegistrants = new RegistrantList();
    107     private RegistrantList gprsDetachedRegistrants = new RegistrantList();
    108     private RegistrantList psRestrictEnabledRegistrants = new RegistrantList();
    109     private RegistrantList psRestrictDisabledRegistrants = new RegistrantList();
    110 
    111     /**
    112      * Sometimes we get the NITZ time before we know what country we
    113      * are in. Keep the time zone information from the NITZ string so
    114      * we can fix the time zone once know the country.
    115      */
    116     private boolean mNeedFixZone = false;
    117     private int mZoneOffset;
    118     private boolean mZoneDst;
    119     private long mZoneTime;
    120     private boolean mGotCountryCode = false;
    121     private ContentResolver cr;
    122 
    123     String mSavedTimeZone;
    124     long mSavedTime;
    125     long mSavedAtTime;
    126 
    127     /**
    128      * We can't register for SIM_RECORDS_LOADED immediately because the
    129      * SIMRecords object may not be instantiated yet.
    130      */
    131     private boolean mNeedToRegForSimLoaded;
    132 
    133     /** Started the recheck process after finding gprs should registerd but not. */
    134     private boolean mStartedGprsRegCheck = false;
    135 
    136     /** Already sent the event-log for no gprs register. */
    137     private boolean mReportedGprsNoReg = false;
    138 
    139     /**
    140      * The Notification object given to the NotificationManager.
    141      */
    142     private Notification mNotification;
    143 
    144     /** Wake lock used while setting time of day. */
    145     private PowerManager.WakeLock mWakeLock;
    146     private static final String WAKELOCK_TAG = "ServiceStateTracker";
    147 
    148     /** Keep track of SPN display rules, so we only broadcast intent if something changes. */
    149     private String curSpn = null;
    150     private String curPlmn = null;
    151     private int curSpnRule = 0;
    152 
    153     /** waiting period before recheck gprs and voice registration. */
    154     static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
    155 
    156     /** Notification type. */
    157     static final int PS_ENABLED = 1001;            // Access Control blocks data service
    158     static final int PS_DISABLED = 1002;           // Access Control enables data service
    159     static final int CS_ENABLED = 1003;            // Access Control blocks all voice/sms service
    160     static final int CS_DISABLED = 1004;           // Access Control enables all voice/sms service
    161     static final int CS_NORMAL_ENABLED = 1005;     // Access Control blocks normal voice/sms service
    162     static final int CS_EMERGENCY_ENABLED = 1006;  // Access Control blocks emergency call service
    163 
    164     /** Notification id. */
    165     static final int PS_NOTIFICATION = 888;  // Id to update and cancel PS restricted
    166     static final int CS_NOTIFICATION = 999;  // Id to update and cancel CS restricted
    167 
    168     static final int MAX_NUM_DATA_STATE_READS = 15;
    169 
    170     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
    171         @Override
    172         public void onReceive(Context context, Intent intent) {
    173             if (intent.getAction().equals(Intent.ACTION_LOCALE_CHANGED)) {
    174                 // update emergency string whenever locale changed
    175                 updateSpnDisplay();
    176             }
    177         }
    178     };
    179 
    180     private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
    181         @Override
    182         public void onChange(boolean selfChange) {
    183             Log.i("GsmServiceStateTracker", "Auto time state changed");
    184             revertToNitz();
    185         }
    186     };
    187 
    188     public GsmServiceStateTracker(GSMPhone phone) {
    189         super();
    190 
    191         this.phone = phone;
    192         cm = phone.mCM;
    193         ss = new ServiceState();
    194         newSS = new ServiceState();
    195         cellLoc = new GsmCellLocation();
    196         newCellLoc = new GsmCellLocation();
    197         rs = new RestrictedState();
    198         mSignalStrength = new SignalStrength();
    199 
    200         PowerManager powerManager =
    201                 (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE);
    202         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
    203 
    204         cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
    205         cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
    206 
    207         cm.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null);
    208         cm.setOnNITZTime(this, EVENT_NITZ_TIME, null);
    209         cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
    210         cm.setOnRestrictedStateChanged(this, EVENT_RESTRICTED_STATE_CHANGED, null);
    211         cm.registerForSIMReady(this, EVENT_SIM_READY, null);
    212 
    213         // system setting property AIRPLANE_MODE_ON is set in Settings.
    214         int airplaneMode = Settings.System.getInt(
    215                 phone.getContext().getContentResolver(),
    216                 Settings.System.AIRPLANE_MODE_ON, 0);
    217         mDesiredPowerState = ! (airplaneMode > 0);
    218 
    219         cr = phone.getContext().getContentResolver();
    220         cr.registerContentObserver(
    221                 Settings.System.getUriFor(Settings.System.AUTO_TIME), true,
    222                 mAutoTimeObserver);
    223         setSignalStrengthDefaultValues();
    224         mNeedToRegForSimLoaded = true;
    225 
    226         // Monitor locale change
    227         IntentFilter filter = new IntentFilter();
    228         filter.addAction(Intent.ACTION_LOCALE_CHANGED);
    229         phone.getContext().registerReceiver(mIntentReceiver, filter);
    230     }
    231 
    232     public void dispose() {
    233         // Unregister for all events.
    234         cm.unregisterForAvailable(this);
    235         cm.unregisterForRadioStateChanged(this);
    236         cm.unregisterForNetworkStateChanged(this);
    237         cm.unregisterForSIMReady(this);
    238 
    239         phone.mSIMRecords.unregisterForRecordsLoaded(this);
    240         cm.unSetOnSignalStrengthUpdate(this);
    241         cm.unSetOnRestrictedStateChanged(this);
    242         cm.unSetOnNITZTime(this);
    243         cr.unregisterContentObserver(this.mAutoTimeObserver);
    244     }
    245 
    246     protected void finalize() {
    247         if(DBG) Log.d(LOG_TAG, "GsmServiceStateTracker finalized");
    248     }
    249 
    250     /**
    251      * Registration point for transition into GPRS attached.
    252      * @param h handler to notify
    253      * @param what what code of message when delivered
    254      * @param obj placed in Message.obj
    255      */
    256     void registerForGprsAttached(Handler h, int what, Object obj) {
    257         Registrant r = new Registrant(h, what, obj);
    258         gprsAttachedRegistrants.add(r);
    259 
    260         if (gprsState == ServiceState.STATE_IN_SERVICE) {
    261             r.notifyRegistrant();
    262         }
    263     }
    264 
    265     void unregisterForGprsAttached(Handler h) {
    266         gprsAttachedRegistrants.remove(h);
    267     }
    268 
    269     void registerForNetworkAttach(Handler h, int what, Object obj) {
    270         Registrant r = new Registrant(h, what, obj);
    271         networkAttachedRegistrants.add(r);
    272 
    273         if (ss.getState() == ServiceState.STATE_IN_SERVICE) {
    274             r.notifyRegistrant();
    275         }
    276     }
    277 
    278     void unregisterForNetworkAttach(Handler h) {
    279         networkAttachedRegistrants.remove(h);
    280     }
    281 
    282     /**
    283      * Registration point for transition into GPRS detached.
    284      * @param h handler to notify
    285      * @param what what code of message when delivered
    286      * @param obj placed in Message.obj
    287      */
    288     void registerForGprsDetached(Handler h, int what, Object obj) {
    289         Registrant r = new Registrant(h, what, obj);
    290         gprsDetachedRegistrants.add(r);
    291 
    292         if (gprsState == ServiceState.STATE_OUT_OF_SERVICE) {
    293             r.notifyRegistrant();
    294         }
    295     }
    296 
    297     void unregisterForGprsDetached(Handler h) {
    298         gprsDetachedRegistrants.remove(h);
    299     }
    300 
    301     /**
    302      * Registration point for transition into packet service restricted zone.
    303      * @param h handler to notify
    304      * @param what what code of message when delivered
    305      * @param obj placed in Message.obj
    306      */
    307     void registerForPsRestrictedEnabled(Handler h, int what, Object obj) {
    308         Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedEnabled ");
    309         Registrant r = new Registrant(h, what, obj);
    310         psRestrictEnabledRegistrants.add(r);
    311 
    312         if (rs.isPsRestricted()) {
    313             r.notifyRegistrant();
    314         }
    315     }
    316 
    317     void unregisterForPsRestrictedEnabled(Handler h) {
    318         psRestrictEnabledRegistrants.remove(h);
    319     }
    320 
    321     /**
    322      * Registration point for transition out of packet service restricted zone.
    323      * @param h handler to notify
    324      * @param what what code of message when delivered
    325      * @param obj placed in Message.obj
    326      */
    327     void registerForPsRestrictedDisabled(Handler h, int what, Object obj) {
    328         Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedDisabled ");
    329         Registrant r = new Registrant(h, what, obj);
    330         psRestrictDisabledRegistrants.add(r);
    331 
    332         if (rs.isPsRestricted()) {
    333             r.notifyRegistrant();
    334         }
    335     }
    336 
    337     void unregisterForPsRestrictedDisabled(Handler h) {
    338         psRestrictDisabledRegistrants.remove(h);
    339     }
    340 
    341     public void handleMessage (Message msg) {
    342         AsyncResult ar;
    343         int[] ints;
    344         String[] strings;
    345         Message message;
    346 
    347         switch (msg.what) {
    348             case EVENT_RADIO_AVAILABLE:
    349                 //this is unnecessary
    350                 //setPowerStateToDesired();
    351                 break;
    352 
    353             case EVENT_SIM_READY:
    354                 // The SIM is now ready i.e if it was locked
    355                 // it has been unlocked. At this stage, the radio is already
    356                 // powered on.
    357                 if (mNeedToRegForSimLoaded) {
    358                     phone.mSIMRecords.registerForRecordsLoaded(this,
    359                             EVENT_SIM_RECORDS_LOADED, null);
    360                     mNeedToRegForSimLoaded = false;
    361                 }
    362                 // restore the previous network selection.
    363                 phone.restoreSavedNetworkSelection(null);
    364                 pollState();
    365                 // Signal strength polling stops when radio is off
    366                 queueNextSignalStrengthPoll();
    367                 break;
    368 
    369             case EVENT_RADIO_STATE_CHANGED:
    370                 // This will do nothing in the radio not
    371                 // available case
    372                 setPowerStateToDesired();
    373                 pollState();
    374                 break;
    375 
    376             case EVENT_NETWORK_STATE_CHANGED:
    377                 pollState();
    378                 break;
    379 
    380             case EVENT_GET_SIGNAL_STRENGTH:
    381                 // This callback is called when signal strength is polled
    382                 // all by itself
    383 
    384                 if (!(cm.getRadioState().isOn()) || (cm.getRadioState().isCdma())) {
    385                     // Polling will continue when radio turns back on and not CDMA
    386                     return;
    387                 }
    388                 ar = (AsyncResult) msg.obj;
    389                 onSignalStrengthResult(ar);
    390                 queueNextSignalStrengthPoll();
    391 
    392                 break;
    393 
    394             case EVENT_GET_LOC_DONE:
    395                 ar = (AsyncResult) msg.obj;
    396 
    397                 if (ar.exception == null) {
    398                     String states[] = (String[])ar.result;
    399                     int lac = -1;
    400                     int cid = -1;
    401                     if (states.length >= 3) {
    402                         try {
    403                             if (states[1] != null && states[1].length() > 0) {
    404                                 lac = Integer.parseInt(states[1], 16);
    405                             }
    406                             if (states[2] != null && states[2].length() > 0) {
    407                                 cid = Integer.parseInt(states[2], 16);
    408                             }
    409                         } catch (NumberFormatException ex) {
    410                             Log.w(LOG_TAG, "error parsing location: " + ex);
    411                         }
    412                     }
    413                     cellLoc.setLacAndCid(lac, cid);
    414                     phone.notifyLocationChanged();
    415                 }
    416 
    417                 // Release any temporary cell lock, which could have been
    418                 // aquired to allow a single-shot location update.
    419                 disableSingleLocationUpdate();
    420                 break;
    421 
    422             case EVENT_POLL_STATE_REGISTRATION:
    423             case EVENT_POLL_STATE_GPRS:
    424             case EVENT_POLL_STATE_OPERATOR:
    425             case EVENT_POLL_STATE_NETWORK_SELECTION_MODE:
    426                 ar = (AsyncResult) msg.obj;
    427 
    428                 handlePollStateResult(msg.what, ar);
    429                 break;
    430 
    431             case EVENT_POLL_SIGNAL_STRENGTH:
    432                 // Just poll signal strength...not part of pollState()
    433 
    434                 cm.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH));
    435                 break;
    436 
    437             case EVENT_NITZ_TIME:
    438                 ar = (AsyncResult) msg.obj;
    439 
    440                 String nitzString = (String)((Object[])ar.result)[0];
    441                 long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue();
    442 
    443                 setTimeFromNITZString(nitzString, nitzReceiveTime);
    444                 break;
    445 
    446             case EVENT_SIGNAL_STRENGTH_UPDATE:
    447                 // This is a notification from
    448                 // CommandsInterface.setOnSignalStrengthUpdate
    449 
    450                 ar = (AsyncResult) msg.obj;
    451 
    452                 // The radio is telling us about signal strength changes
    453                 // we don't have to ask it
    454                 dontPollSignalStrength = true;
    455 
    456                 onSignalStrengthResult(ar);
    457                 break;
    458 
    459             case EVENT_SIM_RECORDS_LOADED:
    460                 updateSpnDisplay();
    461                 break;
    462 
    463             case EVENT_LOCATION_UPDATES_ENABLED:
    464                 ar = (AsyncResult) msg.obj;
    465 
    466                 if (ar.exception == null) {
    467                     cm.getRegistrationState(obtainMessage(EVENT_GET_LOC_DONE, null));
    468                 }
    469                 break;
    470 
    471             case EVENT_SET_PREFERRED_NETWORK_TYPE:
    472                 ar = (AsyncResult) msg.obj;
    473                 // Don't care the result, only use for dereg network (COPS=2)
    474                 message = obtainMessage(EVENT_RESET_PREFERRED_NETWORK_TYPE, ar.userObj);
    475                 cm.setPreferredNetworkType(mPreferredNetworkType, message);
    476                 break;
    477 
    478             case EVENT_RESET_PREFERRED_NETWORK_TYPE:
    479                 ar = (AsyncResult) msg.obj;
    480                 if (ar.userObj != null) {
    481                     AsyncResult.forMessage(((Message) ar.userObj)).exception
    482                             = ar.exception;
    483                     ((Message) ar.userObj).sendToTarget();
    484                 }
    485                 break;
    486 
    487             case EVENT_GET_PREFERRED_NETWORK_TYPE:
    488                 ar = (AsyncResult) msg.obj;
    489 
    490                 if (ar.exception == null) {
    491                     mPreferredNetworkType = ((int[])ar.result)[0];
    492                 } else {
    493                     mPreferredNetworkType = RILConstants.NETWORK_MODE_GLOBAL;
    494                 }
    495 
    496                 message = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE, ar.userObj);
    497                 int toggledNetworkType = RILConstants.NETWORK_MODE_GLOBAL;
    498 
    499                 cm.setPreferredNetworkType(toggledNetworkType, message);
    500                 break;
    501 
    502             case EVENT_CHECK_REPORT_GPRS:
    503                 if (ss != null && !isGprsConsistant(gprsState, ss.getState())) {
    504 
    505                     // Can't register data sevice while voice service is ok
    506                     // i.e. CREG is ok while CGREG is not
    507                     // possible a network or baseband side error
    508                     GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
    509                     EventLog.writeEvent(EventLogTags.DATA_NETWORK_REGISTRATION_FAIL,
    510                             ss.getOperatorNumeric(), loc != null ? loc.getCid() : -1);
    511                     mReportedGprsNoReg = true;
    512                 }
    513                 mStartedGprsRegCheck = false;
    514                 break;
    515 
    516             case EVENT_RESTRICTED_STATE_CHANGED:
    517                 // This is a notification from
    518                 // CommandsInterface.setOnRestrictedStateChanged
    519 
    520                 Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_RESTRICTED_STATE_CHANGED");
    521 
    522                 ar = (AsyncResult) msg.obj;
    523 
    524                 onRestrictedStateChanged(ar);
    525                 break;
    526 
    527             default:
    528                 Log.e(LOG_TAG, "Unhandled message with number: " + msg.what);
    529             break;
    530         }
    531     }
    532 
    533     protected void setPowerStateToDesired() {
    534         // If we want it on and it's off, turn it on
    535         if (mDesiredPowerState
    536             && cm.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
    537             cm.setRadioPower(true, null);
    538         } else if (!mDesiredPowerState && cm.getRadioState().isOn()) {
    539             DataConnectionTracker dcTracker = phone.mDataConnection;
    540             if (! dcTracker.isDataConnectionAsDesired()) {
    541                 EventLog.writeEvent(EventLogTags.DATA_NETWORK_STATUS_ON_RADIO_OFF,
    542                         dcTracker.getStateInString(), dcTracker.getAnyDataEnabled() ? 1 : 0);
    543             }
    544             // If it's on and available and we want it off gracefully
    545             powerOffRadioSafely();
    546         } // Otherwise, we're in the desired state
    547     }
    548 
    549     @Override
    550     protected void powerOffRadioSafely() {
    551         // clean data connection
    552         DataConnectionTracker dcTracker = phone.mDataConnection;
    553         Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION);
    554         msg.arg1 = 1; // tearDown is true
    555         msg.obj = GSMPhone.REASON_RADIO_TURNED_OFF;
    556         dcTracker.sendMessage(msg);
    557 
    558         // poll data state up to 15 times, with a 100ms delay
    559         // totaling 1.5 sec. Normal data disable action will finish in 100ms.
    560         for (int i = 0; i < MAX_NUM_DATA_STATE_READS; i++) {
    561             if (dcTracker.getState() != DataConnectionTracker.State.CONNECTED
    562                     && dcTracker.getState() != DataConnectionTracker.State.DISCONNECTING) {
    563                 Log.d(LOG_TAG, "Data shutdown complete.");
    564                 break;
    565             }
    566             SystemClock.sleep(DATA_STATE_POLL_SLEEP_MS);
    567         }
    568 
    569         // hang up all active voice calls
    570         if (phone.isInCall()) {
    571             phone.mCT.ringingCall.hangupIfAlive();
    572             phone.mCT.backgroundCall.hangupIfAlive();
    573             phone.mCT.foregroundCall.hangupIfAlive();
    574         }
    575 
    576         cm.setRadioPower(false, null);
    577     }
    578 
    579     protected void updateSpnDisplay() {
    580         int rule = phone.mSIMRecords.getDisplayRule(ss.getOperatorNumeric());
    581         String spn = phone.mSIMRecords.getServiceProviderName();
    582         String plmn = ss.getOperatorAlphaLong();
    583 
    584         // For emergency calls only, pass the EmergencyCallsOnly string via EXTRA_PLMN
    585         if (mEmergencyOnly && cm.getRadioState().isOn()) {
    586             plmn = Resources.getSystem().
    587                 getText(com.android.internal.R.string.emergency_calls_only).toString();
    588         }
    589 
    590         if (rule != curSpnRule
    591                 || !TextUtils.equals(spn, curSpn)
    592                 || !TextUtils.equals(plmn, curPlmn)) {
    593             boolean showSpn = !mEmergencyOnly
    594                 && (rule & SIMRecords.SPN_RULE_SHOW_SPN) == SIMRecords.SPN_RULE_SHOW_SPN;
    595             boolean showPlmn =
    596                 (rule & SIMRecords.SPN_RULE_SHOW_PLMN) == SIMRecords.SPN_RULE_SHOW_PLMN;
    597 
    598             Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION);
    599             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
    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     protected void handlePollStateResult (int what, AsyncResult ar) {
    616         int ints[];
    617         String states[];
    618 
    619         // Ignore stale requests from last poll
    620         if (ar.userObj != pollingContext) return;
    621 
    622         if (ar.exception != null) {
    623             CommandException.Error err=null;
    624 
    625             if (ar.exception instanceof CommandException) {
    626                 err = ((CommandException)(ar.exception)).getCommandError();
    627             }
    628 
    629             if (err == CommandException.Error.RADIO_NOT_AVAILABLE) {
    630                 // Radio has crashed or turned off
    631                 cancelPollState();
    632                 return;
    633             }
    634 
    635             if (!cm.getRadioState().isOn()) {
    636                 // Radio has crashed or turned off
    637                 cancelPollState();
    638                 return;
    639             }
    640 
    641             if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW &&
    642                     err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
    643                 Log.e(LOG_TAG,
    644                         "RIL implementation has returned an error where it must succeed" +
    645                         ar.exception);
    646             }
    647         } else try {
    648             switch (what) {
    649                 case EVENT_POLL_STATE_REGISTRATION:
    650                     states = (String[])ar.result;
    651                     int lac = -1;
    652                     int cid = -1;
    653                     int regState = -1;
    654                     if (states.length > 0) {
    655                         try {
    656                             regState = Integer.parseInt(states[0]);
    657                             if (states.length >= 3) {
    658                                 if (states[1] != null && states[1].length() > 0) {
    659                                     lac = Integer.parseInt(states[1], 16);
    660                                 }
    661                                 if (states[2] != null && states[2].length() > 0) {
    662                                     cid = Integer.parseInt(states[2], 16);
    663                                 }
    664                             }
    665                         } catch (NumberFormatException ex) {
    666                             Log.w(LOG_TAG, "error parsing RegistrationState: " + ex);
    667                         }
    668                     }
    669 
    670                     mGsmRoaming = regCodeIsRoaming(regState);
    671                     newSS.setState (regCodeToServiceState(regState));
    672 
    673                     if (regState == 10 || regState == 12 || regState == 13 || regState == 14) {
    674                         mEmergencyOnly = true;
    675                     } else {
    676                         mEmergencyOnly = false;
    677                     }
    678 
    679                     // LAC and CID are -1 if not avail
    680                     newCellLoc.setLacAndCid(lac, cid);
    681                 break;
    682 
    683                 case EVENT_POLL_STATE_GPRS:
    684                     states = (String[])ar.result;
    685 
    686                     int type = 0;
    687                     regState = -1;
    688                     if (states.length > 0) {
    689                         try {
    690                             regState = Integer.parseInt(states[0]);
    691 
    692                             // states[3] (if present) is the current radio technology
    693                             if (states.length >= 4 && states[3] != null) {
    694                                 type = Integer.parseInt(states[3]);
    695                             }
    696                         } catch (NumberFormatException ex) {
    697                             Log.w(LOG_TAG, "error parsing GprsRegistrationState: " + ex);
    698                         }
    699                     }
    700                     newGPRSState = regCodeToServiceState(regState);
    701                     mDataRoaming = regCodeIsRoaming(regState);
    702                     newNetworkType = type;
    703                     newSS.setRadioTechnology(type);
    704                 break;
    705 
    706                 case EVENT_POLL_STATE_OPERATOR:
    707                     String opNames[] = (String[])ar.result;
    708 
    709                     if (opNames != null && opNames.length >= 3) {
    710                         newSS.setOperatorName (
    711                                 opNames[0], opNames[1], opNames[2]);
    712                     }
    713                 break;
    714 
    715                 case EVENT_POLL_STATE_NETWORK_SELECTION_MODE:
    716                     ints = (int[])ar.result;
    717                     newSS.setIsManualSelection(ints[0] == 1);
    718                 break;
    719             }
    720 
    721         } catch (RuntimeException ex) {
    722             Log.e(LOG_TAG, "Exception while polling service state. "
    723                             + "Probably malformed RIL response.", ex);
    724         }
    725 
    726         pollingContext[0]--;
    727 
    728         if (pollingContext[0] == 0) {
    729             /**
    730              *  Since the roaming states of gsm service (from +CREG) and
    731              *  data service (from +CGREG) could be different, the new SS
    732              *  is set roaming while either one is roaming.
    733              *
    734              *  There is an exception for the above rule. The new SS is not set
    735              *  as roaming while gsm service reports roaming but indeed it is
    736              *  not roaming between operators.
    737              */
    738             boolean roaming = (mGsmRoaming || mDataRoaming);
    739             if (mGsmRoaming && !isRoamingBetweenOperators(mGsmRoaming, newSS)) {
    740                 roaming = false;
    741             }
    742             newSS.setRoaming(roaming);
    743             newSS.setEmergencyOnly(mEmergencyOnly);
    744             pollStateDone();
    745         }
    746     }
    747 
    748     private void setSignalStrengthDefaultValues() {
    749         mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, true);
    750     }
    751 
    752     /**
    753      * A complete "service state" from our perspective is
    754      * composed of a handful of separate requests to the radio.
    755      *
    756      * We make all of these requests at once, but then abandon them
    757      * and start over again if the radio notifies us that some
    758      * event has changed
    759      */
    760     private void pollState() {
    761         pollingContext = new int[1];
    762         pollingContext[0] = 0;
    763 
    764         switch (cm.getRadioState()) {
    765             case RADIO_UNAVAILABLE:
    766                 newSS.setStateOutOfService();
    767                 newCellLoc.setStateInvalid();
    768                 setSignalStrengthDefaultValues();
    769                 mGotCountryCode = false;
    770                 pollStateDone();
    771             break;
    772 
    773             case RADIO_OFF:
    774                 newSS.setStateOff();
    775                 newCellLoc.setStateInvalid();
    776                 setSignalStrengthDefaultValues();
    777                 mGotCountryCode = false;
    778                 pollStateDone();
    779             break;
    780 
    781             case RUIM_NOT_READY:
    782             case RUIM_READY:
    783             case RUIM_LOCKED_OR_ABSENT:
    784             case NV_NOT_READY:
    785             case NV_READY:
    786                 Log.d(LOG_TAG, "Radio Technology Change ongoing, setting SS to off");
    787                 newSS.setStateOff();
    788                 newCellLoc.setStateInvalid();
    789                 setSignalStrengthDefaultValues();
    790                 mGotCountryCode = false;
    791 
    792                 //NOTE: pollStateDone() is not needed in this case
    793                 break;
    794 
    795             default:
    796                 // Issue all poll-related commands at once
    797                 // then count down the responses, which
    798                 // are allowed to arrive out-of-order
    799 
    800                 pollingContext[0]++;
    801                 cm.getOperator(
    802                     obtainMessage(
    803                         EVENT_POLL_STATE_OPERATOR, pollingContext));
    804 
    805                 pollingContext[0]++;
    806                 cm.getGPRSRegistrationState(
    807                     obtainMessage(
    808                         EVENT_POLL_STATE_GPRS, pollingContext));
    809 
    810                 pollingContext[0]++;
    811                 cm.getRegistrationState(
    812                     obtainMessage(
    813                         EVENT_POLL_STATE_REGISTRATION, pollingContext));
    814 
    815                 pollingContext[0]++;
    816                 cm.getNetworkSelectionMode(
    817                     obtainMessage(
    818                         EVENT_POLL_STATE_NETWORK_SELECTION_MODE, pollingContext));
    819             break;
    820         }
    821     }
    822 
    823     private static String networkTypeToString(int type) {
    824         //Network Type from GPRS_REGISTRATION_STATE
    825         String ret = "unknown";
    826 
    827         switch (type) {
    828             case DATA_ACCESS_GPRS:
    829                 ret = "GPRS";
    830                 break;
    831             case DATA_ACCESS_EDGE:
    832                 ret = "EDGE";
    833                 break;
    834             case DATA_ACCESS_UMTS:
    835                 ret = "UMTS";
    836                 break;
    837             case DATA_ACCESS_HSDPA:
    838                 ret = "HSDPA";
    839                 break;
    840             case DATA_ACCESS_HSUPA:
    841                 ret = "HSUPA";
    842                 break;
    843             case DATA_ACCESS_HSPA:
    844                 ret = "HSPA";
    845                 break;
    846             default:
    847                 Log.e(LOG_TAG, "Wrong network type: " + Integer.toString(type));
    848                 break;
    849         }
    850 
    851         return ret;
    852     }
    853 
    854     private void pollStateDone() {
    855         if (DBG) {
    856             Log.d(LOG_TAG, "Poll ServiceState done: " +
    857                 " oldSS=[" + ss + "] newSS=[" + newSS +
    858                 "] oldGprs=" + gprsState + " newGprs=" + newGPRSState +
    859                 " oldType=" + networkTypeToString(networkType) +
    860                 " newType=" + networkTypeToString(newNetworkType));
    861         }
    862 
    863         boolean hasRegistered =
    864             ss.getState() != ServiceState.STATE_IN_SERVICE
    865             && newSS.getState() == ServiceState.STATE_IN_SERVICE;
    866 
    867         boolean hasDeregistered =
    868             ss.getState() == ServiceState.STATE_IN_SERVICE
    869             && newSS.getState() != ServiceState.STATE_IN_SERVICE;
    870 
    871         boolean hasGprsAttached =
    872                 gprsState != ServiceState.STATE_IN_SERVICE
    873                 && newGPRSState == ServiceState.STATE_IN_SERVICE;
    874 
    875         boolean hasGprsDetached =
    876                 gprsState == ServiceState.STATE_IN_SERVICE
    877                 && newGPRSState != ServiceState.STATE_IN_SERVICE;
    878 
    879         boolean hasNetworkTypeChanged = networkType != newNetworkType;
    880 
    881         boolean hasChanged = !newSS.equals(ss);
    882 
    883         boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming();
    884 
    885         boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming();
    886 
    887         boolean hasLocationChanged = !newCellLoc.equals(cellLoc);
    888 
    889         // Add an event log when connection state changes
    890         if (ss.getState() != newSS.getState() || gprsState != newGPRSState) {
    891             EventLog.writeEvent(EventLogTags.GSM_SERVICE_STATE_CHANGE,
    892                 ss.getState(), gprsState, newSS.getState(), newGPRSState);
    893         }
    894 
    895         ServiceState tss;
    896         tss = ss;
    897         ss = newSS;
    898         newSS = tss;
    899         // clean slate for next time
    900         newSS.setStateOutOfService();
    901 
    902         GsmCellLocation tcl = cellLoc;
    903         cellLoc = newCellLoc;
    904         newCellLoc = tcl;
    905 
    906         // Add an event log when network type switched
    907         // TODO: we may add filtering to reduce the event logged,
    908         // i.e. check preferred network setting, only switch to 2G, etc
    909         if (hasNetworkTypeChanged) {
    910             int cid = -1;
    911             GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
    912             if (loc != null) cid = loc.getCid();
    913             EventLog.writeEvent(EventLogTags.GSM_RAT_SWITCHED, cid, networkType, newNetworkType);
    914             Log.d(LOG_TAG,
    915                     "RAT switched " + networkTypeToString(networkType) + " -> "
    916                     + networkTypeToString(newNetworkType) + " at cell " + cid);
    917         }
    918 
    919         gprsState = newGPRSState;
    920         networkType = newNetworkType;
    921 
    922         newSS.setStateOutOfService(); // clean slate for next time
    923 
    924         if (hasNetworkTypeChanged) {
    925             phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
    926                     networkTypeToString(networkType));
    927         }
    928 
    929         if (hasRegistered) {
    930             networkAttachedRegistrants.notifyRegistrants();
    931         }
    932 
    933         if (hasChanged) {
    934             String operatorNumeric;
    935 
    936             updateSpnDisplay();
    937 
    938             phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA,
    939                 ss.getOperatorAlphaLong());
    940 
    941             operatorNumeric = ss.getOperatorNumeric();
    942             phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric);
    943 
    944             if (operatorNumeric == null) {
    945                 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
    946             } else {
    947                 String iso = "";
    948                 try{
    949                     iso = MccTable.countryCodeForMcc(Integer.parseInt(
    950                             operatorNumeric.substring(0,3)));
    951                 } catch ( NumberFormatException ex){
    952                     Log.w(LOG_TAG, "countryCodeForMcc error" + ex);
    953                 } catch ( StringIndexOutOfBoundsException ex) {
    954                     Log.w(LOG_TAG, "countryCodeForMcc error" + ex);
    955                 }
    956 
    957                 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, iso);
    958                 mGotCountryCode = true;
    959 
    960                 if (mNeedFixZone) {
    961                     TimeZone zone = null;
    962                     // If the offset is (0, false) and the timezone property
    963                     // is set, use the timezone property rather than
    964                     // GMT.
    965                     String zoneName = SystemProperties.get(TIMEZONE_PROPERTY);
    966                     if ((mZoneOffset == 0) && (mZoneDst == false) &&
    967                         (zoneName != null) && (zoneName.length() > 0) &&
    968                         (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)) {
    969                         zone = TimeZone.getDefault();
    970                         // For NITZ string without timezone,
    971                         // need adjust time to reflect default timezone setting
    972                         long tzOffset;
    973                         tzOffset = zone.getOffset(System.currentTimeMillis());
    974                         if (getAutoTime()) {
    975                             setAndBroadcastNetworkSetTime(System.currentTimeMillis() - tzOffset);
    976                         } else {
    977                             // Adjust the saved NITZ time to account for tzOffset.
    978                             mSavedTime = mSavedTime - tzOffset;
    979                         }
    980                     } else if (iso.equals("")){
    981                         // Country code not found.  This is likely a test network.
    982                         // Get a TimeZone based only on the NITZ parameters (best guess).
    983                         zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime);
    984                     } else {
    985                         zone = TimeUtils.getTimeZone(mZoneOffset,
    986                             mZoneDst, mZoneTime, iso);
    987                     }
    988 
    989                     mNeedFixZone = false;
    990 
    991                     if (zone != null) {
    992                         if (getAutoTime()) {
    993                             setAndBroadcastNetworkSetTimeZone(zone.getID());
    994                         }
    995                         saveNitzTimeZone(zone.getID());
    996                     }
    997                 }
    998             }
    999 
   1000             phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
   1001                 ss.getRoaming() ? "true" : "false");
   1002 
   1003             phone.notifyServiceStateChanged(ss);
   1004         }
   1005 
   1006         if (hasGprsAttached) {
   1007             gprsAttachedRegistrants.notifyRegistrants();
   1008         }
   1009 
   1010         if (hasGprsDetached) {
   1011             gprsDetachedRegistrants.notifyRegistrants();
   1012         }
   1013 
   1014         if (hasNetworkTypeChanged) {
   1015             phone.notifyDataConnection(null);
   1016         }
   1017 
   1018         if (hasRoamingOn) {
   1019             roamingOnRegistrants.notifyRegistrants();
   1020         }
   1021 
   1022         if (hasRoamingOff) {
   1023             roamingOffRegistrants.notifyRegistrants();
   1024         }
   1025 
   1026         if (hasLocationChanged) {
   1027             phone.notifyLocationChanged();
   1028         }
   1029 
   1030         if (! isGprsConsistant(gprsState, ss.getState())) {
   1031             if (!mStartedGprsRegCheck && !mReportedGprsNoReg) {
   1032                 mStartedGprsRegCheck = true;
   1033 
   1034                 int check_period = Settings.Secure.getInt(
   1035                         phone.getContext().getContentResolver(),
   1036                         Settings.Secure.GPRS_REGISTER_CHECK_PERIOD_MS,
   1037                         DEFAULT_GPRS_CHECK_PERIOD_MILLIS);
   1038                 sendMessageDelayed(obtainMessage(EVENT_CHECK_REPORT_GPRS),
   1039                         check_period);
   1040             }
   1041         } else {
   1042             mReportedGprsNoReg = false;
   1043         }
   1044     }
   1045 
   1046     /**
   1047      * Check if GPRS got registred while voice is registered
   1048      *
   1049      * @param gprsState for GPRS registration state, i.e. CGREG in GSM
   1050      * @param serviceState for voice registration state, i.e. CREG in GSM
   1051      * @return false if device only register to voice but not gprs
   1052      */
   1053     private boolean isGprsConsistant (int gprsState, int serviceState) {
   1054         return !((serviceState == ServiceState.STATE_IN_SERVICE) &&
   1055                 (gprsState != ServiceState.STATE_IN_SERVICE));
   1056     }
   1057 
   1058     /**
   1059      * Returns a TimeZone object based only on parameters from the NITZ string.
   1060      */
   1061     private TimeZone getNitzTimeZone(int offset, boolean dst, long when) {
   1062         TimeZone guess = findTimeZone(offset, dst, when);
   1063         if (guess == null) {
   1064             // Couldn't find a proper timezone.  Perhaps the DST data is wrong.
   1065             guess = findTimeZone(offset, !dst, when);
   1066         }
   1067         if (DBG) {
   1068             Log.d(LOG_TAG, "getNitzTimeZone returning "
   1069                     + (guess == null ? guess : guess.getID()));
   1070         }
   1071         return guess;
   1072     }
   1073 
   1074     private TimeZone findTimeZone(int offset, boolean dst, long when) {
   1075         int rawOffset = offset;
   1076         if (dst) {
   1077             rawOffset -= 3600000;
   1078         }
   1079         String[] zones = TimeZone.getAvailableIDs(rawOffset);
   1080         TimeZone guess = null;
   1081         Date d = new Date(when);
   1082         for (String zone : zones) {
   1083             TimeZone tz = TimeZone.getTimeZone(zone);
   1084             if (tz.getOffset(when) == offset &&
   1085                 tz.inDaylightTime(d) == dst) {
   1086                 guess = tz;
   1087                 break;
   1088             }
   1089         }
   1090 
   1091         return guess;
   1092     }
   1093 
   1094     private void queueNextSignalStrengthPoll() {
   1095         if (dontPollSignalStrength || (cm.getRadioState().isCdma())) {
   1096             // The radio is telling us about signal strength changes
   1097             // we don't have to ask it
   1098             return;
   1099         }
   1100 
   1101         Message msg;
   1102 
   1103         msg = obtainMessage();
   1104         msg.what = EVENT_POLL_SIGNAL_STRENGTH;
   1105 
   1106         long nextTime;
   1107 
   1108         // TODO Done't poll signal strength if screen is off
   1109         sendMessageDelayed(msg, POLL_PERIOD_MILLIS);
   1110     }
   1111 
   1112     /**
   1113      *  send signal-strength-changed notification if changed
   1114      *  Called both for solicited and unsolicited signal stength updates
   1115      */
   1116     private void onSignalStrengthResult(AsyncResult ar) {
   1117         SignalStrength oldSignalStrength = mSignalStrength;
   1118         int rssi = 99;
   1119 
   1120         if (ar.exception != null) {
   1121             // -1 = unknown
   1122             // most likely radio is resetting/disconnected
   1123             setSignalStrengthDefaultValues();
   1124         } else {
   1125             int[] ints = (int[])ar.result;
   1126 
   1127             // bug 658816 seems to be a case where the result is 0-length
   1128             if (ints.length != 0) {
   1129                 rssi = ints[0];
   1130             } else {
   1131                 Log.e(LOG_TAG, "Bogus signal strength response");
   1132                 rssi = 99;
   1133             }
   1134         }
   1135 
   1136         mSignalStrength = new SignalStrength(rssi, -1, -1, -1,
   1137                 -1, -1, -1, true);
   1138 
   1139         if (!mSignalStrength.equals(oldSignalStrength)) {
   1140             try { // This takes care of delayed EVENT_POLL_SIGNAL_STRENGTH (scheduled after
   1141                   // POLL_PERIOD_MILLIS) during Radio Technology Change)
   1142                 phone.notifySignalStrength();
   1143            } catch (NullPointerException ex) {
   1144                 log("onSignalStrengthResult() Phone already destroyed: " + ex
   1145                         + "SignalStrength not notified");
   1146            }
   1147         }
   1148     }
   1149 
   1150     /**
   1151      * Set restricted state based on the OnRestrictedStateChanged notification
   1152      * If any voice or packet restricted state changes, trigger a UI
   1153      * notification and notify registrants when sim is ready.
   1154      *
   1155      * @param ar an int value of RIL_RESTRICTED_STATE_*
   1156      */
   1157     private void onRestrictedStateChanged(AsyncResult ar) {
   1158         Log.d(LOG_TAG, "[DSAC DEB] " + "onRestrictedStateChanged");
   1159         RestrictedState newRs = new RestrictedState();
   1160 
   1161         Log.d(LOG_TAG, "[DSAC DEB] " + "current rs at enter "+ rs);
   1162 
   1163         if (ar.exception == null) {
   1164             int[] ints = (int[])ar.result;
   1165             int state = ints[0];
   1166 
   1167             newRs.setCsEmergencyRestricted(
   1168                     ((state & RILConstants.RIL_RESTRICTED_STATE_CS_EMERGENCY) != 0) ||
   1169                     ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) );
   1170             //ignore the normal call and data restricted state before SIM READY
   1171             if (phone.getIccCard().getState() == IccCard.State.READY) {
   1172                 newRs.setCsNormalRestricted(
   1173                         ((state & RILConstants.RIL_RESTRICTED_STATE_CS_NORMAL) != 0) ||
   1174                         ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) );
   1175                 newRs.setPsRestricted(
   1176                         (state & RILConstants.RIL_RESTRICTED_STATE_PS_ALL)!= 0);
   1177             }
   1178 
   1179             Log.d(LOG_TAG, "[DSAC DEB] " + "new rs "+ newRs);
   1180 
   1181             if (!rs.isPsRestricted() && newRs.isPsRestricted()) {
   1182                 psRestrictEnabledRegistrants.notifyRegistrants();
   1183                 setNotification(PS_ENABLED);
   1184             } else if (rs.isPsRestricted() && !newRs.isPsRestricted()) {
   1185                 psRestrictDisabledRegistrants.notifyRegistrants();
   1186                 setNotification(PS_DISABLED);
   1187             }
   1188 
   1189             /**
   1190              * There are two kind of cs restriction, normal and emergency. So
   1191              * there are 4 x 4 combinations in current and new restricted states
   1192              * and we only need to notify when state is changed.
   1193              */
   1194             if (rs.isCsRestricted()) {
   1195                 if (!newRs.isCsRestricted()) {
   1196                     // remove all restriction
   1197                     setNotification(CS_DISABLED);
   1198                 } else if (!newRs.isCsNormalRestricted()) {
   1199                     // remove normal restriction
   1200                     setNotification(CS_EMERGENCY_ENABLED);
   1201                 } else if (!newRs.isCsEmergencyRestricted()) {
   1202                     // remove emergency restriction
   1203                     setNotification(CS_NORMAL_ENABLED);
   1204                 }
   1205             } else if (rs.isCsEmergencyRestricted() && !rs.isCsNormalRestricted()) {
   1206                 if (!newRs.isCsRestricted()) {
   1207                     // remove all restriction
   1208                     setNotification(CS_DISABLED);
   1209                 } else if (newRs.isCsRestricted()) {
   1210                     // enable all restriction
   1211                     setNotification(CS_ENABLED);
   1212                 } else if (newRs.isCsNormalRestricted()) {
   1213                     // remove emergency restriction and enable normal restriction
   1214                     setNotification(CS_NORMAL_ENABLED);
   1215                 }
   1216             } else if (!rs.isCsEmergencyRestricted() && rs.isCsNormalRestricted()) {
   1217                 if (!newRs.isCsRestricted()) {
   1218                     // remove all restriction
   1219                     setNotification(CS_DISABLED);
   1220                 } else if (newRs.isCsRestricted()) {
   1221                     // enable all restriction
   1222                     setNotification(CS_ENABLED);
   1223                 } else if (newRs.isCsEmergencyRestricted()) {
   1224                     // remove normal restriction and enable emergency restriction
   1225                     setNotification(CS_EMERGENCY_ENABLED);
   1226                 }
   1227             } else {
   1228                 if (newRs.isCsRestricted()) {
   1229                     // enable all restriction
   1230                     setNotification(CS_ENABLED);
   1231                 } else if (newRs.isCsEmergencyRestricted()) {
   1232                     // enable emergency restriction
   1233                     setNotification(CS_EMERGENCY_ENABLED);
   1234                 } else if (newRs.isCsNormalRestricted()) {
   1235                     // enable normal restriction
   1236                     setNotification(CS_NORMAL_ENABLED);
   1237                 }
   1238             }
   1239 
   1240             rs = newRs;
   1241         }
   1242         Log.d(LOG_TAG, "[DSAC DEB] " + "current rs at return "+ rs);
   1243     }
   1244 
   1245     /** code is registration state 0-5 from TS 27.007 7.2 */
   1246     private int regCodeToServiceState(int code) {
   1247         switch (code) {
   1248             case 0:
   1249             case 2: // 2 is "searching"
   1250             case 3: // 3 is "registration denied"
   1251             case 4: // 4 is "unknown" no vaild in current baseband
   1252             case 10:// same as 0, but indicates that emergency call is possible.
   1253             case 12:// same as 2, but indicates that emergency call is possible.
   1254             case 13:// same as 3, but indicates that emergency call is possible.
   1255             case 14:// same as 4, but indicates that emergency call is possible.
   1256                 return ServiceState.STATE_OUT_OF_SERVICE;
   1257 
   1258             case 1:
   1259                 return ServiceState.STATE_IN_SERVICE;
   1260 
   1261             case 5:
   1262                 // in service, roam
   1263                 return ServiceState.STATE_IN_SERVICE;
   1264 
   1265             default:
   1266                 Log.w(LOG_TAG, "unexpected service state " + code);
   1267                 return ServiceState.STATE_OUT_OF_SERVICE;
   1268         }
   1269     }
   1270 
   1271 
   1272     /**
   1273      * code is registration state 0-5 from TS 27.007 7.2
   1274      * returns true if registered roam, false otherwise
   1275      */
   1276     private boolean regCodeIsRoaming (int code) {
   1277         // 5 is  "in service -- roam"
   1278         return 5 == code;
   1279     }
   1280 
   1281     /**
   1282      * Set roaming state when gsmRoaming is true and, if operator mcc is the
   1283      * same as sim mcc, ons is different from spn
   1284      * @param gsmRoaming TS 27.007 7.2 CREG registered roaming
   1285      * @param s ServiceState hold current ons
   1286      * @return true for roaming state set
   1287      */
   1288     private boolean isRoamingBetweenOperators(boolean gsmRoaming, ServiceState s) {
   1289         String spn = SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "empty");
   1290 
   1291         String onsl = s.getOperatorAlphaLong();
   1292         String onss = s.getOperatorAlphaShort();
   1293 
   1294         boolean equalsOnsl = onsl != null && spn.equals(onsl);
   1295         boolean equalsOnss = onss != null && spn.equals(onss);
   1296 
   1297         String simNumeric = SystemProperties.get(
   1298                 TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
   1299         String  operatorNumeric = s.getOperatorNumeric();
   1300 
   1301         boolean equalsMcc = true;
   1302         try {
   1303             equalsMcc = simNumeric.substring(0, 3).
   1304                     equals(operatorNumeric.substring(0, 3));
   1305         } catch (Exception e){
   1306         }
   1307 
   1308         return gsmRoaming && !(equalsMcc && (equalsOnsl || equalsOnss));
   1309     }
   1310 
   1311     private static int twoDigitsAt(String s, int offset) {
   1312         int a, b;
   1313 
   1314         a = Character.digit(s.charAt(offset), 10);
   1315         b = Character.digit(s.charAt(offset+1), 10);
   1316 
   1317         if (a < 0 || b < 0) {
   1318 
   1319             throw new RuntimeException("invalid format");
   1320         }
   1321 
   1322         return a*10 + b;
   1323     }
   1324 
   1325     /**
   1326      * @return The current GPRS state. IN_SERVICE is the same as "attached"
   1327      * and OUT_OF_SERVICE is the same as detached.
   1328      */
   1329     int getCurrentGprsState() {
   1330         return gprsState;
   1331     }
   1332 
   1333     /**
   1334      * @return true if phone is camping on a technology (eg UMTS)
   1335      * that could support voice and data simultaniously.
   1336      */
   1337     boolean isConcurrentVoiceAndData() {
   1338         return (networkType >= DATA_ACCESS_UMTS);
   1339     }
   1340 
   1341     /**
   1342      * Provides the name of the algorithmic time zone for the specified
   1343      * offset.  Taken from TimeZone.java.
   1344      */
   1345     private static String displayNameFor(int off) {
   1346         off = off / 1000 / 60;
   1347 
   1348         char[] buf = new char[9];
   1349         buf[0] = 'G';
   1350         buf[1] = 'M';
   1351         buf[2] = 'T';
   1352 
   1353         if (off < 0) {
   1354             buf[3] = '-';
   1355             off = -off;
   1356         } else {
   1357             buf[3] = '+';
   1358         }
   1359 
   1360         int hours = off / 60;
   1361         int minutes = off % 60;
   1362 
   1363         buf[4] = (char) ('0' + hours / 10);
   1364         buf[5] = (char) ('0' + hours % 10);
   1365 
   1366         buf[6] = ':';
   1367 
   1368         buf[7] = (char) ('0' + minutes / 10);
   1369         buf[8] = (char) ('0' + minutes % 10);
   1370 
   1371         return new String(buf);
   1372     }
   1373 
   1374     /**
   1375      * nitzReceiveTime is time_t that the NITZ time was posted
   1376      */
   1377     private void setTimeFromNITZString (String nitz, long nitzReceiveTime) {
   1378         // "yy/mm/dd,hh:mm:ss(+/-)tz"
   1379         // tz is in number of quarter-hours
   1380 
   1381         long start = SystemClock.elapsedRealtime();
   1382         Log.i(LOG_TAG, "NITZ: " + nitz + "," + nitzReceiveTime +
   1383                         " start=" + start + " delay=" + (start - nitzReceiveTime));
   1384 
   1385         try {
   1386             /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
   1387              * offset as well (which we won't worry about until later) */
   1388             Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
   1389 
   1390             c.clear();
   1391             c.set(Calendar.DST_OFFSET, 0);
   1392 
   1393             String[] nitzSubs = nitz.split("[/:,+-]");
   1394 
   1395             int year = 2000 + Integer.parseInt(nitzSubs[0]);
   1396             c.set(Calendar.YEAR, year);
   1397 
   1398             // month is 0 based!
   1399             int month = Integer.parseInt(nitzSubs[1]) - 1;
   1400             c.set(Calendar.MONTH, month);
   1401 
   1402             int date = Integer.parseInt(nitzSubs[2]);
   1403             c.set(Calendar.DATE, date);
   1404 
   1405             int hour = Integer.parseInt(nitzSubs[3]);
   1406             c.set(Calendar.HOUR, hour);
   1407 
   1408             int minute = Integer.parseInt(nitzSubs[4]);
   1409             c.set(Calendar.MINUTE, minute);
   1410 
   1411             int second = Integer.parseInt(nitzSubs[5]);
   1412             c.set(Calendar.SECOND, second);
   1413 
   1414             boolean sign = (nitz.indexOf('-') == -1);
   1415 
   1416             int tzOffset = Integer.parseInt(nitzSubs[6]);
   1417 
   1418             int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7])
   1419                                               : 0;
   1420 
   1421             // The zone offset received from NITZ is for current local time,
   1422             // so DST correction is already applied.  Don't add it again.
   1423             //
   1424             // tzOffset += dst * 4;
   1425             //
   1426             // We could unapply it if we wanted the raw offset.
   1427 
   1428             tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000;
   1429 
   1430             TimeZone    zone = null;
   1431 
   1432             // As a special extension, the Android emulator appends the name of
   1433             // the host computer's timezone to the nitz string. this is zoneinfo
   1434             // timezone name of the form Area!Location or Area!Location!SubLocation
   1435             // so we need to convert the ! into /
   1436             if (nitzSubs.length >= 9) {
   1437                 String  tzname = nitzSubs[8].replace('!','/');
   1438                 zone = TimeZone.getTimeZone( tzname );
   1439             }
   1440 
   1441             String iso = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY);
   1442 
   1443             if (zone == null) {
   1444 
   1445                 if (mGotCountryCode) {
   1446                     if (iso != null && iso.length() > 0) {
   1447                         zone = TimeUtils.getTimeZone(tzOffset, dst != 0,
   1448                                 c.getTimeInMillis(),
   1449                                 iso);
   1450                     } else {
   1451                         // We don't have a valid iso country code.  This is
   1452                         // most likely because we're on a test network that's
   1453                         // using a bogus MCC (eg, "001"), so get a TimeZone
   1454                         // based only on the NITZ parameters.
   1455                         zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis());
   1456                     }
   1457                 }
   1458             }
   1459 
   1460             if (zone == null) {
   1461                 // We got the time before the country, so we don't know
   1462                 // how to identify the DST rules yet.  Save the information
   1463                 // and hope to fix it up later.
   1464 
   1465                 mNeedFixZone = true;
   1466                 mZoneOffset  = tzOffset;
   1467                 mZoneDst     = dst != 0;
   1468                 mZoneTime    = c.getTimeInMillis();
   1469             }
   1470 
   1471             if (zone != null) {
   1472                 if (getAutoTime()) {
   1473                     setAndBroadcastNetworkSetTimeZone(zone.getID());
   1474                 }
   1475                 saveNitzTimeZone(zone.getID());
   1476             }
   1477 
   1478             String ignore = SystemProperties.get("gsm.ignore-nitz");
   1479             if (ignore != null && ignore.equals("yes")) {
   1480                 Log.i(LOG_TAG, "NITZ: Not setting clock because gsm.ignore-nitz is set");
   1481                 return;
   1482             }
   1483 
   1484             try {
   1485                 mWakeLock.acquire();
   1486 
   1487                 if (getAutoTime()) {
   1488                     long millisSinceNitzReceived
   1489                             = SystemClock.elapsedRealtime() - nitzReceiveTime;
   1490 
   1491                     if (millisSinceNitzReceived < 0) {
   1492                         // Sanity check: something is wrong
   1493                         Log.i(LOG_TAG, "NITZ: not setting time, clock has rolled "
   1494                                             + "backwards since NITZ time was received, "
   1495                                             + nitz);
   1496                         return;
   1497                     }
   1498 
   1499                     if (millisSinceNitzReceived > Integer.MAX_VALUE) {
   1500                         // If the time is this far off, something is wrong > 24 days!
   1501                         Log.i(LOG_TAG, "NITZ: not setting time, processing has taken "
   1502                                         + (millisSinceNitzReceived / (1000 * 60 * 60 * 24))
   1503                                         + " days");
   1504                         return;
   1505                     }
   1506 
   1507                     // Note: with range checks above, cast to int is safe
   1508                     c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived);
   1509 
   1510                     Log.i(LOG_TAG, "NITZ: Setting time of day to " + c.getTime()
   1511                         + " NITZ receive delay(ms): " + millisSinceNitzReceived
   1512                         + " gained(ms): "
   1513                         + (c.getTimeInMillis() - System.currentTimeMillis())
   1514                         + " from " + nitz);
   1515 
   1516                     setAndBroadcastNetworkSetTime(c.getTimeInMillis());
   1517                     Log.i(LOG_TAG, "NITZ: after Setting time of day");
   1518                 }
   1519                 SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis()));
   1520                 saveNitzTime(c.getTimeInMillis());
   1521                 if (Config.LOGV) {
   1522                     long end = SystemClock.elapsedRealtime();
   1523                     Log.v(LOG_TAG, "NITZ: end=" + end + " dur=" + (end - start));
   1524                 }
   1525             } finally {
   1526                 mWakeLock.release();
   1527             }
   1528         } catch (RuntimeException ex) {
   1529             Log.e(LOG_TAG, "NITZ: Parsing NITZ time " + nitz, ex);
   1530         }
   1531     }
   1532 
   1533     private boolean getAutoTime() {
   1534         try {
   1535             return Settings.System.getInt(phone.getContext().getContentResolver(),
   1536                     Settings.System.AUTO_TIME) > 0;
   1537         } catch (SettingNotFoundException snfe) {
   1538             return true;
   1539         }
   1540     }
   1541 
   1542     private void saveNitzTimeZone(String zoneId) {
   1543         mSavedTimeZone = zoneId;
   1544     }
   1545 
   1546     private void saveNitzTime(long time) {
   1547         mSavedTime = time;
   1548         mSavedAtTime = SystemClock.elapsedRealtime();
   1549     }
   1550 
   1551     /**
   1552      * Set the timezone and send out a sticky broadcast so the system can
   1553      * determine if the timezone was set by the carrier.
   1554      *
   1555      * @param zoneId timezone set by carrier
   1556      */
   1557     private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
   1558         AlarmManager alarm =
   1559             (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE);
   1560         alarm.setTimeZone(zoneId);
   1561         Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
   1562         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1563         intent.putExtra("time-zone", zoneId);
   1564         phone.getContext().sendStickyBroadcast(intent);
   1565     }
   1566 
   1567     /**
   1568      * Set the time and Send out a sticky broadcast so the system can determine
   1569      * if the time was set by the carrier.
   1570      *
   1571      * @param time time set by network
   1572      */
   1573     private void setAndBroadcastNetworkSetTime(long time) {
   1574         SystemClock.setCurrentTimeMillis(time);
   1575         Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
   1576         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1577         intent.putExtra("time", time);
   1578         phone.getContext().sendStickyBroadcast(intent);
   1579     }
   1580 
   1581     private void revertToNitz() {
   1582         if (Settings.System.getInt(phone.getContext().getContentResolver(),
   1583                 Settings.System.AUTO_TIME, 0) == 0) {
   1584             return;
   1585         }
   1586         Log.d(LOG_TAG, "Reverting to NITZ: tz='" + mSavedTimeZone
   1587                 + "' mSavedTime=" + mSavedTime
   1588                 + " mSavedAtTime=" + mSavedAtTime);
   1589         if (mSavedTimeZone != null && mSavedTime != 0 && mSavedAtTime != 0) {
   1590             setAndBroadcastNetworkSetTimeZone(mSavedTimeZone);
   1591             setAndBroadcastNetworkSetTime(mSavedTime
   1592                     + (SystemClock.elapsedRealtime() - mSavedAtTime));
   1593         }
   1594     }
   1595 
   1596     /**
   1597      * Post a notification to NotificationManager for restricted state
   1598      *
   1599      * @param notifyType is one state of PS/CS_*_ENABLE/DISABLE
   1600      */
   1601     private void setNotification(int notifyType) {
   1602 
   1603         Log.d(LOG_TAG, "[DSAC DEB] " + "create notification " + notifyType);
   1604         Context context = phone.getContext();
   1605 
   1606         mNotification = new Notification();
   1607         mNotification.when = System.currentTimeMillis();
   1608         mNotification.flags = Notification.FLAG_AUTO_CANCEL;
   1609         mNotification.icon = com.android.internal.R.drawable.stat_sys_warning;
   1610         Intent intent = new Intent();
   1611         mNotification.contentIntent = PendingIntent
   1612         .getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
   1613 
   1614         CharSequence details = "";
   1615         CharSequence title = context.getText(com.android.internal.R.string.RestrictedChangedTitle);
   1616         int notificationId = CS_NOTIFICATION;
   1617 
   1618         switch (notifyType) {
   1619         case PS_ENABLED:
   1620             notificationId = PS_NOTIFICATION;
   1621             details = context.getText(com.android.internal.R.string.RestrictedOnData);;
   1622             break;
   1623         case PS_DISABLED:
   1624             notificationId = PS_NOTIFICATION;
   1625             break;
   1626         case CS_ENABLED:
   1627             details = context.getText(com.android.internal.R.string.RestrictedOnAllVoice);;
   1628             break;
   1629         case CS_NORMAL_ENABLED:
   1630             details = context.getText(com.android.internal.R.string.RestrictedOnNormal);;
   1631             break;
   1632         case CS_EMERGENCY_ENABLED:
   1633             details = context.getText(com.android.internal.R.string.RestrictedOnEmergency);;
   1634             break;
   1635         case CS_DISABLED:
   1636             // do nothing and cancel the notification later
   1637             break;
   1638         }
   1639 
   1640         Log.d(LOG_TAG, "[DSAC DEB] " + "put notification " + title + " / " +details);
   1641         mNotification.tickerText = title;
   1642         mNotification.setLatestEventInfo(context, title, details,
   1643                 mNotification.contentIntent);
   1644 
   1645         NotificationManager notificationManager = (NotificationManager)
   1646             context.getSystemService(Context.NOTIFICATION_SERVICE);
   1647 
   1648         if (notifyType == PS_DISABLED || notifyType == CS_DISABLED) {
   1649             // cancel previous post notification
   1650             notificationManager.cancel(notificationId);
   1651         } else {
   1652             // update restricted state notification
   1653             notificationManager.notify(notificationId, mNotification);
   1654         }
   1655     }
   1656 
   1657     private void log(String s) {
   1658         Log.d(LOG_TAG, "[GsmServiceStateTracker] " + s);
   1659     }
   1660 }
   1661