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