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