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