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