Home | History | Annotate | Download | only in telephony
      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;
     18 
     19 import android.os.AsyncResult;
     20 import android.os.Handler;
     21 import android.os.Message;
     22 import android.os.Registrant;
     23 import android.os.RegistrantList;
     24 import android.os.SystemClock;
     25 import android.telephony.CellInfo;
     26 import android.telephony.ServiceState;
     27 import android.telephony.SignalStrength;
     28 import android.util.TimeUtils;
     29 
     30 import java.io.FileDescriptor;
     31 import java.io.PrintWriter;
     32 import java.util.ArrayList;
     33 import java.util.List;
     34 
     35 import com.android.internal.telephony.dataconnection.DcTrackerBase;
     36 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
     37 import com.android.internal.telephony.uicc.IccRecords;
     38 import com.android.internal.telephony.uicc.UiccCardApplication;
     39 import com.android.internal.telephony.uicc.UiccController;
     40 
     41 /**
     42  * {@hide}
     43  */
     44 public abstract class ServiceStateTracker extends Handler {
     45     protected  static final boolean DBG = true;
     46     protected static final boolean VDBG = false;
     47 
     48     protected static final String PROP_FORCE_ROAMING = "telephony.test.forceRoaming";
     49 
     50     protected CommandsInterface mCi;
     51     protected UiccController mUiccController = null;
     52     protected UiccCardApplication mUiccApplcation = null;
     53     protected IccRecords mIccRecords = null;
     54 
     55     protected PhoneBase mPhoneBase;
     56 
     57     protected boolean mVoiceCapable;
     58 
     59     public ServiceState mSS = new ServiceState();
     60     protected ServiceState mNewSS = new ServiceState();
     61 
     62     private static final long LAST_CELL_INFO_LIST_MAX_AGE_MS = 2000;
     63     protected long mLastCellInfoListTime;
     64     protected List<CellInfo> mLastCellInfoList = null;
     65 
     66     // This is final as subclasses alias to a more specific type
     67     // so we don't want the reference to change.
     68     protected final CellInfo mCellInfo;
     69 
     70     protected SignalStrength mSignalStrength = new SignalStrength();
     71 
     72     // TODO - this should not be public, right now used externally GsmConnetion.
     73     public RestrictedState mRestrictedState = new RestrictedState();
     74 
     75     /* The otaspMode passed to PhoneStateListener#onOtaspChanged */
     76     static public final int OTASP_UNINITIALIZED = 0;
     77     static public final int OTASP_UNKNOWN = 1;
     78     static public final int OTASP_NEEDED = 2;
     79     static public final int OTASP_NOT_NEEDED = 3;
     80 
     81     /**
     82      * A unique identifier to track requests associated with a poll
     83      * and ignore stale responses.  The value is a count-down of
     84      * expected responses in this pollingContext.
     85      */
     86     protected int[] mPollingContext;
     87     protected boolean mDesiredPowerState;
     88 
     89     /**
     90      * By default, strength polling is enabled.  However, if we're
     91      * getting unsolicited signal strength updates from the radio, set
     92      * value to true and don't bother polling any more.
     93      */
     94     protected boolean mDontPollSignalStrength = false;
     95 
     96     protected RegistrantList mRoamingOnRegistrants = new RegistrantList();
     97     protected RegistrantList mRoamingOffRegistrants = new RegistrantList();
     98     protected RegistrantList mAttachedRegistrants = new RegistrantList();
     99     protected RegistrantList mDetachedRegistrants = new RegistrantList();
    100     protected RegistrantList mNetworkAttachedRegistrants = new RegistrantList();
    101     protected RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList();
    102     protected RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList();
    103 
    104     /* Radio power off pending flag and tag counter */
    105     private boolean mPendingRadioPowerOffAfterDataOff = false;
    106     private int mPendingRadioPowerOffAfterDataOffTag = 0;
    107 
    108     /** Signal strength poll rate. */
    109     protected static final int POLL_PERIOD_MILLIS = 20 * 1000;
    110 
    111     /** Waiting period before recheck gprs and voice registration. */
    112     public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
    113 
    114     /** GSM events */
    115     protected static final int EVENT_RADIO_STATE_CHANGED               = 1;
    116     protected static final int EVENT_NETWORK_STATE_CHANGED             = 2;
    117     protected static final int EVENT_GET_SIGNAL_STRENGTH               = 3;
    118     protected static final int EVENT_POLL_STATE_REGISTRATION           = 4;
    119     protected static final int EVENT_POLL_STATE_GPRS                   = 5;
    120     protected static final int EVENT_POLL_STATE_OPERATOR               = 6;
    121     protected static final int EVENT_POLL_SIGNAL_STRENGTH              = 10;
    122     protected static final int EVENT_NITZ_TIME                         = 11;
    123     protected static final int EVENT_SIGNAL_STRENGTH_UPDATE            = 12;
    124     protected static final int EVENT_RADIO_AVAILABLE                   = 13;
    125     protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14;
    126     protected static final int EVENT_GET_LOC_DONE                      = 15;
    127     protected static final int EVENT_SIM_RECORDS_LOADED                = 16;
    128     protected static final int EVENT_SIM_READY                         = 17;
    129     protected static final int EVENT_LOCATION_UPDATES_ENABLED          = 18;
    130     protected static final int EVENT_GET_PREFERRED_NETWORK_TYPE        = 19;
    131     protected static final int EVENT_SET_PREFERRED_NETWORK_TYPE        = 20;
    132     protected static final int EVENT_RESET_PREFERRED_NETWORK_TYPE      = 21;
    133     protected static final int EVENT_CHECK_REPORT_GPRS                 = 22;
    134     protected static final int EVENT_RESTRICTED_STATE_CHANGED          = 23;
    135 
    136     /** CDMA events */
    137     protected static final int EVENT_POLL_STATE_REGISTRATION_CDMA      = 24;
    138     protected static final int EVENT_POLL_STATE_OPERATOR_CDMA          = 25;
    139     protected static final int EVENT_RUIM_READY                        = 26;
    140     protected static final int EVENT_RUIM_RECORDS_LOADED               = 27;
    141     protected static final int EVENT_POLL_SIGNAL_STRENGTH_CDMA         = 28;
    142     protected static final int EVENT_GET_SIGNAL_STRENGTH_CDMA          = 29;
    143     protected static final int EVENT_NETWORK_STATE_CHANGED_CDMA        = 30;
    144     protected static final int EVENT_GET_LOC_DONE_CDMA                 = 31;
    145     //protected static final int EVENT_UNUSED                            = 32;
    146     protected static final int EVENT_NV_LOADED                         = 33;
    147     protected static final int EVENT_POLL_STATE_CDMA_SUBSCRIPTION      = 34;
    148     protected static final int EVENT_NV_READY                          = 35;
    149     protected static final int EVENT_ERI_FILE_LOADED                   = 36;
    150     protected static final int EVENT_OTA_PROVISION_STATUS_CHANGE       = 37;
    151     protected static final int EVENT_SET_RADIO_POWER_OFF               = 38;
    152     protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED  = 39;
    153     protected static final int EVENT_CDMA_PRL_VERSION_CHANGED          = 40;
    154     protected static final int EVENT_RADIO_ON                          = 41;
    155     protected static final int EVENT_ICC_CHANGED                       = 42;
    156     protected static final int EVENT_GET_CELL_INFO_LIST                = 43;
    157     protected static final int EVENT_UNSOL_CELL_INFO_LIST              = 44;
    158 
    159     protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
    160 
    161     /**
    162      * List of ISO codes for countries that can have an offset of
    163      * GMT+0 when not in daylight savings time.  This ignores some
    164      * small places such as the Canary Islands (Spain) and
    165      * Danmarkshavn (Denmark).  The list must be sorted by code.
    166     */
    167     protected static final String[] GMT_COUNTRY_CODES = {
    168         "bf", // Burkina Faso
    169         "ci", // Cote d'Ivoire
    170         "eh", // Western Sahara
    171         "fo", // Faroe Islands, Denmark
    172         "gb", // United Kingdom of Great Britain and Northern Ireland
    173         "gh", // Ghana
    174         "gm", // Gambia
    175         "gn", // Guinea
    176         "gw", // Guinea Bissau
    177         "ie", // Ireland
    178         "lr", // Liberia
    179         "is", // Iceland
    180         "ma", // Morocco
    181         "ml", // Mali
    182         "mr", // Mauritania
    183         "pt", // Portugal
    184         "sl", // Sierra Leone
    185         "sn", // Senegal
    186         "st", // Sao Tome and Principe
    187         "tg", // Togo
    188     };
    189 
    190     private class CellInfoResult {
    191         List<CellInfo> list;
    192         Object lockObj = new Object();
    193     }
    194 
    195     /** Reason for registration denial. */
    196     protected static final String REGISTRATION_DENIED_GEN  = "General";
    197     protected static final String REGISTRATION_DENIED_AUTH = "Authentication Failure";
    198 
    199     protected ServiceStateTracker(PhoneBase phoneBase, CommandsInterface ci, CellInfo cellInfo) {
    200         mPhoneBase = phoneBase;
    201         mCellInfo = cellInfo;
    202         mCi = ci;
    203         mVoiceCapable = mPhoneBase.getContext().getResources().getBoolean(
    204                 com.android.internal.R.bool.config_voice_capable);
    205         mUiccController = UiccController.getInstance();
    206         mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
    207         mCi.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
    208         mCi.registerForCellInfoList(this, EVENT_UNSOL_CELL_INFO_LIST, null);
    209     }
    210 
    211     public void dispose() {
    212         mCi.unSetOnSignalStrengthUpdate(this);
    213         mUiccController.unregisterForIccChanged(this);
    214         mCi.unregisterForCellInfoList(this);
    215     }
    216 
    217     public boolean getDesiredPowerState() {
    218         return mDesiredPowerState;
    219     }
    220 
    221     private SignalStrength mLastSignalStrength = null;
    222     protected boolean notifySignalStrength() {
    223         boolean notified = false;
    224         synchronized(mCellInfo) {
    225             if (!mSignalStrength.equals(mLastSignalStrength)) {
    226                 try {
    227                     mPhoneBase.notifySignalStrength();
    228                     notified = true;
    229                 } catch (NullPointerException ex) {
    230                     loge("updateSignalStrength() Phone already destroyed: " + ex
    231                             + "SignalStrength not notified");
    232                 }
    233             }
    234         }
    235         return notified;
    236     }
    237 
    238     /**
    239      * Some operators have been known to report registration failure
    240      * data only devices, to fix that use DataRegState.
    241      */
    242     protected void useDataRegStateForDataOnlyDevices() {
    243         if (mVoiceCapable == false) {
    244             if (DBG) {
    245                 log("useDataRegStateForDataOnlyDevice: VoiceRegState=" + mNewSS.getVoiceRegState()
    246                     + " DataRegState=" + mNewSS.getDataRegState());
    247             }
    248             // TODO: Consider not lying and instead have callers know the difference.
    249             mNewSS.setVoiceRegState(mNewSS.getDataRegState());
    250         }
    251     }
    252 
    253 
    254     /**
    255      * Registration point for combined roaming on
    256      * combined roaming is true when roaming is true and ONS differs SPN
    257      *
    258      * @param h handler to notify
    259      * @param what what code of message when delivered
    260      * @param obj placed in Message.obj
    261      */
    262     public  void registerForRoamingOn(Handler h, int what, Object obj) {
    263         Registrant r = new Registrant(h, what, obj);
    264         mRoamingOnRegistrants.add(r);
    265 
    266         if (mSS.getRoaming()) {
    267             r.notifyRegistrant();
    268         }
    269     }
    270 
    271     public  void unregisterForRoamingOn(Handler h) {
    272         mRoamingOnRegistrants.remove(h);
    273     }
    274 
    275     /**
    276      * Registration point for combined roaming off
    277      * combined roaming is true when roaming is true and ONS differs SPN
    278      *
    279      * @param h handler to notify
    280      * @param what what code of message when delivered
    281      * @param obj placed in Message.obj
    282      */
    283     public  void registerForRoamingOff(Handler h, int what, Object obj) {
    284         Registrant r = new Registrant(h, what, obj);
    285         mRoamingOffRegistrants.add(r);
    286 
    287         if (!mSS.getRoaming()) {
    288             r.notifyRegistrant();
    289         }
    290     }
    291 
    292     public  void unregisterForRoamingOff(Handler h) {
    293         mRoamingOffRegistrants.remove(h);
    294     }
    295 
    296     /**
    297      * Re-register network by toggling preferred network type.
    298      * This is a work-around to deregister and register network since there is
    299      * no ril api to set COPS=2 (deregister) only.
    300      *
    301      * @param onComplete is dispatched when this is complete.  it will be
    302      * an AsyncResult, and onComplete.obj.exception will be non-null
    303      * on failure.
    304      */
    305     public void reRegisterNetwork(Message onComplete) {
    306         mCi.getPreferredNetworkType(
    307                 obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete));
    308     }
    309 
    310     public void
    311     setRadioPower(boolean power) {
    312         mDesiredPowerState = power;
    313 
    314         setPowerStateToDesired();
    315     }
    316 
    317     /**
    318      * These two flags manage the behavior of the cell lock -- the
    319      * lock should be held if either flag is true.  The intention is
    320      * to allow temporary acquisition of the lock to get a single
    321      * update.  Such a lock grab and release can thus be made to not
    322      * interfere with more permanent lock holds -- in other words, the
    323      * lock will only be released if both flags are false, and so
    324      * releases by temporary users will only affect the lock state if
    325      * there is no continuous user.
    326      */
    327     private boolean mWantContinuousLocationUpdates;
    328     private boolean mWantSingleLocationUpdate;
    329 
    330     public void enableSingleLocationUpdate() {
    331         if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
    332         mWantSingleLocationUpdate = true;
    333         mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
    334     }
    335 
    336     public void enableLocationUpdates() {
    337         if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
    338         mWantContinuousLocationUpdates = true;
    339         mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
    340     }
    341 
    342     protected void disableSingleLocationUpdate() {
    343         mWantSingleLocationUpdate = false;
    344         if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
    345             mCi.setLocationUpdates(false, null);
    346         }
    347     }
    348 
    349     public void disableLocationUpdates() {
    350         mWantContinuousLocationUpdates = false;
    351         if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
    352             mCi.setLocationUpdates(false, null);
    353         }
    354     }
    355 
    356     @Override
    357     public void handleMessage(Message msg) {
    358         switch (msg.what) {
    359             case EVENT_SET_RADIO_POWER_OFF:
    360                 synchronized(this) {
    361                     if (mPendingRadioPowerOffAfterDataOff &&
    362                             (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) {
    363                         if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
    364                         hangupAndPowerOff();
    365                         mPendingRadioPowerOffAfterDataOffTag += 1;
    366                         mPendingRadioPowerOffAfterDataOff = false;
    367                     } else {
    368                         log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 +
    369                                 "!= tag=" + mPendingRadioPowerOffAfterDataOffTag);
    370                     }
    371                 }
    372                 break;
    373 
    374             case EVENT_ICC_CHANGED:
    375                 onUpdateIccAvailability();
    376                 break;
    377 
    378             case EVENT_GET_CELL_INFO_LIST: {
    379                 AsyncResult ar = (AsyncResult) msg.obj;
    380                 CellInfoResult result = (CellInfoResult) ar.userObj;
    381                 synchronized(result.lockObj) {
    382                     if (ar.exception != null) {
    383                         log("EVENT_GET_CELL_INFO_LIST: error ret null, e=" + ar.exception);
    384                         result.list = null;
    385                     } else {
    386                         result.list = (List<CellInfo>) ar.result;
    387 
    388                         if (VDBG) {
    389                             log("EVENT_GET_CELL_INFO_LIST: size=" + result.list.size()
    390                                     + " list=" + result.list);
    391                         }
    392                     }
    393                     mLastCellInfoListTime = SystemClock.elapsedRealtime();
    394                     mLastCellInfoList = result.list;
    395                     result.lockObj.notify();
    396                 }
    397                 break;
    398             }
    399 
    400             case EVENT_UNSOL_CELL_INFO_LIST: {
    401                 AsyncResult ar = (AsyncResult) msg.obj;
    402                 if (ar.exception != null) {
    403                     log("EVENT_UNSOL_CELL_INFO_LIST: error ignoring, e=" + ar.exception);
    404                 } else {
    405                     List<CellInfo> list = (List<CellInfo>) ar.result;
    406                     if (DBG) {
    407                         log("EVENT_UNSOL_CELL_INFO_LIST: size=" + list.size()
    408                                 + " list=" + list);
    409                     }
    410                     mLastCellInfoListTime = SystemClock.elapsedRealtime();
    411                     mLastCellInfoList = list;
    412                     mPhoneBase.notifyCellInfo(list);
    413                 }
    414                 break;
    415             }
    416 
    417             default:
    418                 log("Unhandled message with number: " + msg.what);
    419                 break;
    420         }
    421     }
    422 
    423     protected abstract Phone getPhone();
    424     protected abstract void handlePollStateResult(int what, AsyncResult ar);
    425     protected abstract void updateSpnDisplay();
    426     protected abstract void setPowerStateToDesired();
    427     protected abstract void onUpdateIccAvailability();
    428     protected abstract void log(String s);
    429     protected abstract void loge(String s);
    430 
    431     public abstract int getCurrentDataConnectionState();
    432     public abstract boolean isConcurrentVoiceAndDataAllowed();
    433 
    434     /**
    435      * Registration point for transition into DataConnection attached.
    436      * @param h handler to notify
    437      * @param what what code of message when delivered
    438      * @param obj placed in Message.obj
    439      */
    440     public void registerForDataConnectionAttached(Handler h, int what, Object obj) {
    441         Registrant r = new Registrant(h, what, obj);
    442         mAttachedRegistrants.add(r);
    443 
    444         if (getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
    445             r.notifyRegistrant();
    446         }
    447     }
    448     public void unregisterForDataConnectionAttached(Handler h) {
    449         mAttachedRegistrants.remove(h);
    450     }
    451 
    452     /**
    453      * Registration point for transition into DataConnection detached.
    454      * @param h handler to notify
    455      * @param what what code of message when delivered
    456      * @param obj placed in Message.obj
    457      */
    458     public void registerForDataConnectionDetached(Handler h, int what, Object obj) {
    459         Registrant r = new Registrant(h, what, obj);
    460         mDetachedRegistrants.add(r);
    461 
    462         if (getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
    463             r.notifyRegistrant();
    464         }
    465     }
    466     public void unregisterForDataConnectionDetached(Handler h) {
    467         mDetachedRegistrants.remove(h);
    468     }
    469 
    470     /**
    471      * Registration point for transition into network attached.
    472      * @param h handler to notify
    473      * @param what what code of message when delivered
    474      * @param obj in Message.obj
    475      */
    476     public void registerForNetworkAttached(Handler h, int what, Object obj) {
    477         Registrant r = new Registrant(h, what, obj);
    478 
    479         mNetworkAttachedRegistrants.add(r);
    480         if (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
    481             r.notifyRegistrant();
    482         }
    483     }
    484     public void unregisterForNetworkAttached(Handler h) {
    485         mNetworkAttachedRegistrants.remove(h);
    486     }
    487 
    488     /**
    489      * Registration point for transition into packet service restricted zone.
    490      * @param h handler to notify
    491      * @param what what code of message when delivered
    492      * @param obj placed in Message.obj
    493      */
    494     public void registerForPsRestrictedEnabled(Handler h, int what, Object obj) {
    495         Registrant r = new Registrant(h, what, obj);
    496         mPsRestrictEnabledRegistrants.add(r);
    497 
    498         if (mRestrictedState.isPsRestricted()) {
    499             r.notifyRegistrant();
    500         }
    501     }
    502 
    503     public void unregisterForPsRestrictedEnabled(Handler h) {
    504         mPsRestrictEnabledRegistrants.remove(h);
    505     }
    506 
    507     /**
    508      * Registration point for transition out of packet service restricted zone.
    509      * @param h handler to notify
    510      * @param what what code of message when delivered
    511      * @param obj placed in Message.obj
    512      */
    513     public void registerForPsRestrictedDisabled(Handler h, int what, Object obj) {
    514         Registrant r = new Registrant(h, what, obj);
    515         mPsRestrictDisabledRegistrants.add(r);
    516 
    517         if (mRestrictedState.isPsRestricted()) {
    518             r.notifyRegistrant();
    519         }
    520     }
    521 
    522     public void unregisterForPsRestrictedDisabled(Handler h) {
    523         mPsRestrictDisabledRegistrants.remove(h);
    524     }
    525 
    526     /**
    527      * Clean up existing voice and data connection then turn off radio power.
    528      *
    529      * Hang up the existing voice calls to decrease call drop rate.
    530      */
    531     public void powerOffRadioSafely(DcTrackerBase dcTracker) {
    532         synchronized (this) {
    533             if (!mPendingRadioPowerOffAfterDataOff) {
    534                 // To minimize race conditions we call cleanUpAllConnections on
    535                 // both if else paths instead of before this isDisconnected test.
    536                 if (dcTracker.isDisconnected()) {
    537                     // To minimize race conditions we do this after isDisconnected
    538                     dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
    539                     if (DBG) log("Data disconnected, turn off radio right away.");
    540                     hangupAndPowerOff();
    541                 } else {
    542                     dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
    543                     Message msg = Message.obtain(this);
    544                     msg.what = EVENT_SET_RADIO_POWER_OFF;
    545                     msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
    546                     if (sendMessageDelayed(msg, 30000)) {
    547                         if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
    548                         mPendingRadioPowerOffAfterDataOff = true;
    549                     } else {
    550                         log("Cannot send delayed Msg, turn off radio right away.");
    551                         hangupAndPowerOff();
    552                     }
    553                 }
    554             }
    555         }
    556     }
    557 
    558     /**
    559      * process the pending request to turn radio off after data is disconnected
    560      *
    561      * return true if there is pending request to process; false otherwise.
    562      */
    563     public boolean processPendingRadioPowerOffAfterDataOff() {
    564         synchronized(this) {
    565             if (mPendingRadioPowerOffAfterDataOff) {
    566                 if (DBG) log("Process pending request to turn radio off.");
    567                 mPendingRadioPowerOffAfterDataOffTag += 1;
    568                 hangupAndPowerOff();
    569                 mPendingRadioPowerOffAfterDataOff = false;
    570                 return true;
    571             }
    572             return false;
    573         }
    574     }
    575 
    576     /**
    577      * send signal-strength-changed notification if changed Called both for
    578      * solicited and unsolicited signal strength updates
    579      *
    580      * @return true if the signal strength changed and a notification was sent.
    581      */
    582     protected boolean onSignalStrengthResult(AsyncResult ar, boolean isGsm) {
    583         SignalStrength oldSignalStrength = mSignalStrength;
    584 
    585         // This signal is used for both voice and data radio signal so parse
    586         // all fields
    587 
    588         if ((ar.exception == null) && (ar.result != null)) {
    589             mSignalStrength = (SignalStrength) ar.result;
    590             mSignalStrength.validateInput();
    591             mSignalStrength.setGsm(isGsm);
    592         } else {
    593             log("onSignalStrengthResult() Exception from RIL : " + ar.exception);
    594             mSignalStrength = new SignalStrength(isGsm);
    595         }
    596 
    597         return notifySignalStrength();
    598     }
    599 
    600     /**
    601      * Hang up all voice call and turn off radio. Implemented by derived class.
    602      */
    603     protected abstract void hangupAndPowerOff();
    604 
    605     /** Cancel a pending (if any) pollState() operation */
    606     protected void cancelPollState() {
    607         // This will effectively cancel the rest of the poll requests.
    608         mPollingContext = new int[1];
    609     }
    610 
    611     /**
    612      * Return true if time zone needs fixing.
    613      *
    614      * @param phoneBase
    615      * @param operatorNumeric
    616      * @param prevOperatorNumeric
    617      * @param needToFixTimeZone
    618      * @return true if time zone needs to be fixed
    619      */
    620     protected boolean shouldFixTimeZoneNow(PhoneBase phoneBase, String operatorNumeric,
    621             String prevOperatorNumeric, boolean needToFixTimeZone) {
    622         // Return false if the mcc isn't valid as we don't know where we are.
    623         // Return true if we have an IccCard and the mcc changed or we
    624         // need to fix it because when the NITZ time came in we didn't
    625         // know the country code.
    626 
    627         // If mcc is invalid then we'll return false
    628         int mcc;
    629         try {
    630             mcc = Integer.parseInt(operatorNumeric.substring(0, 3));
    631         } catch (Exception e) {
    632             if (DBG) {
    633                 log("shouldFixTimeZoneNow: no mcc, operatorNumeric=" + operatorNumeric +
    634                         " retVal=false");
    635             }
    636             return false;
    637         }
    638 
    639         // If prevMcc is invalid will make it different from mcc
    640         // so we'll return true if the card exists.
    641         int prevMcc;
    642         try {
    643             prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3));
    644         } catch (Exception e) {
    645             prevMcc = mcc + 1;
    646         }
    647 
    648         // Determine if the Icc card exists
    649         boolean iccCardExist = false;
    650         if (mUiccApplcation != null) {
    651             iccCardExist = mUiccApplcation.getState() != AppState.APPSTATE_UNKNOWN;
    652         }
    653 
    654         // Determine retVal
    655         boolean retVal = ((iccCardExist && (mcc != prevMcc)) || needToFixTimeZone);
    656         if (DBG) {
    657             long ctm = System.currentTimeMillis();
    658             log("shouldFixTimeZoneNow: retVal=" + retVal +
    659                     " iccCardExist=" + iccCardExist +
    660                     " operatorNumeric=" + operatorNumeric + " mcc=" + mcc +
    661                     " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc +
    662                     " needToFixTimeZone=" + needToFixTimeZone +
    663                     " ltod=" + TimeUtils.logTimeOfDay(ctm));
    664         }
    665         return retVal;
    666     }
    667 
    668     /**
    669      * @return all available cell information or null if none.
    670      */
    671     public List<CellInfo> getAllCellInfo() {
    672         CellInfoResult result = new CellInfoResult();
    673         if (VDBG) log("SST.getAllCellInfo(): E");
    674         int ver = mCi.getRilVersion();
    675         if (ver >= 8) {
    676             if (isCallerOnDifferentThread()) {
    677                 if ((SystemClock.elapsedRealtime() - mLastCellInfoListTime)
    678                         > LAST_CELL_INFO_LIST_MAX_AGE_MS) {
    679                     Message msg = obtainMessage(EVENT_GET_CELL_INFO_LIST, result);
    680                     synchronized(result.lockObj) {
    681                         mCi.getCellInfoList(msg);
    682                         try {
    683                             result.lockObj.wait();
    684                         } catch (InterruptedException e) {
    685                             e.printStackTrace();
    686                             result.list = null;
    687                         }
    688                     }
    689                 } else {
    690                     if (DBG) log("SST.getAllCellInfo(): return last, back to back calls");
    691                     result.list = mLastCellInfoList;
    692                 }
    693             } else {
    694                 if (DBG) log("SST.getAllCellInfo(): return last, same thread can't block");
    695                 result.list = mLastCellInfoList;
    696             }
    697         } else {
    698             if (DBG) log("SST.getAllCellInfo(): not implemented");
    699             result.list = null;
    700         }
    701         if (DBG) {
    702             if (result.list != null) {
    703                 log("SST.getAllCellInfo(): X size=" + result.list.size()
    704                         + " list=" + result.list);
    705             } else {
    706                 log("SST.getAllCellInfo(): X size=0 list=null");
    707             }
    708         }
    709         return result.list;
    710     }
    711 
    712     /**
    713      * @return signal strength
    714      */
    715     public SignalStrength getSignalStrength() {
    716         synchronized(mCellInfo) {
    717             return mSignalStrength;
    718         }
    719     }
    720 
    721     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    722         pw.println("ServiceStateTracker:");
    723         pw.println(" mSS=" + mSS);
    724         pw.println(" mNewSS=" + mNewSS);
    725         pw.println(" mCellInfo=" + mCellInfo);
    726         pw.println(" mRestrictedState=" + mRestrictedState);
    727         pw.println(" mPollingContext=" + mPollingContext);
    728         pw.println(" mDesiredPowerState=" + mDesiredPowerState);
    729         pw.println(" mDontPollSignalStrength=" + mDontPollSignalStrength);
    730         pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff);
    731         pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag);
    732     }
    733 
    734     /**
    735      * Verifies the current thread is the same as the thread originally
    736      * used in the initialization of this instance. Throws RuntimeException
    737      * if not.
    738      *
    739      * @exception RuntimeException if the current thread is not
    740      * the thread that originally obtained this PhoneBase instance.
    741      */
    742     protected void checkCorrectThread() {
    743         if (Thread.currentThread() != getLooper().getThread()) {
    744             throw new RuntimeException(
    745                     "ServiceStateTracker must be used from within one thread");
    746         }
    747     }
    748 
    749     protected boolean isCallerOnDifferentThread() {
    750         boolean value = Thread.currentThread() != getLooper().getThread();
    751         if (VDBG) log("isCallerOnDifferentThread: " + value);
    752         return value;
    753     }
    754 }
    755