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.telephony.ServiceState;
     25 import android.telephony.SignalStrength;
     26 import android.util.TimeUtils;
     27 
     28 import java.io.FileDescriptor;
     29 import java.io.PrintWriter;
     30 
     31 /**
     32  * {@hide}
     33  */
     34 public abstract class ServiceStateTracker extends Handler {
     35 
     36     protected CommandsInterface cm;
     37 
     38     public ServiceState ss;
     39     protected ServiceState newSS;
     40 
     41     public SignalStrength mSignalStrength;
     42 
     43     // TODO - this should not be public
     44     public RestrictedState mRestrictedState = new RestrictedState();
     45 
     46     /* The otaspMode passed to PhoneStateListener#onOtaspChanged */
     47     static public final int OTASP_UNINITIALIZED = 0;
     48     static public final int OTASP_UNKNOWN = 1;
     49     static public final int OTASP_NEEDED = 2;
     50     static public final int OTASP_NOT_NEEDED = 3;
     51 
     52     /**
     53      * A unique identifier to track requests associated with a poll
     54      * and ignore stale responses.  The value is a count-down of
     55      * expected responses in this pollingContext.
     56      */
     57     protected int[] pollingContext;
     58     protected boolean mDesiredPowerState;
     59 
     60     /**
     61      *  Values correspond to ServiceState.RIL_RADIO_TECHNOLOGY_ definitions.
     62      */
     63     protected int mRilRadioTechnology = 0;
     64     protected int mNewRilRadioTechnology = 0;
     65 
     66     /**
     67      * By default, strength polling is enabled.  However, if we're
     68      * getting unsolicited signal strength updates from the radio, set
     69      * value to true and don't bother polling any more.
     70      */
     71     protected boolean dontPollSignalStrength = false;
     72 
     73     protected RegistrantList mRoamingOnRegistrants = new RegistrantList();
     74     protected RegistrantList mRoamingOffRegistrants = new RegistrantList();
     75     protected RegistrantList mAttachedRegistrants = new RegistrantList();
     76     protected RegistrantList mDetachedRegistrants = new RegistrantList();
     77     protected RegistrantList mNetworkAttachedRegistrants = new RegistrantList();
     78     protected RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList();
     79     protected RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList();
     80 
     81     /* Radio power off pending flag and tag counter */
     82     private boolean mPendingRadioPowerOffAfterDataOff = false;
     83     private int mPendingRadioPowerOffAfterDataOffTag = 0;
     84 
     85     protected  static final boolean DBG = true;
     86 
     87     /** Signal strength poll rate. */
     88     protected static final int POLL_PERIOD_MILLIS = 20 * 1000;
     89 
     90     /** Waiting period before recheck gprs and voice registration. */
     91     public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
     92 
     93     /** GSM events */
     94     protected static final int EVENT_RADIO_STATE_CHANGED               = 1;
     95     protected static final int EVENT_NETWORK_STATE_CHANGED             = 2;
     96     protected static final int EVENT_GET_SIGNAL_STRENGTH               = 3;
     97     protected static final int EVENT_POLL_STATE_REGISTRATION           = 4;
     98     protected static final int EVENT_POLL_STATE_GPRS                   = 5;
     99     protected static final int EVENT_POLL_STATE_OPERATOR               = 6;
    100     protected static final int EVENT_POLL_SIGNAL_STRENGTH              = 10;
    101     protected static final int EVENT_NITZ_TIME                         = 11;
    102     protected static final int EVENT_SIGNAL_STRENGTH_UPDATE            = 12;
    103     protected static final int EVENT_RADIO_AVAILABLE                   = 13;
    104     protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14;
    105     protected static final int EVENT_GET_LOC_DONE                      = 15;
    106     protected static final int EVENT_SIM_RECORDS_LOADED                = 16;
    107     protected static final int EVENT_SIM_READY                         = 17;
    108     protected static final int EVENT_LOCATION_UPDATES_ENABLED          = 18;
    109     protected static final int EVENT_GET_PREFERRED_NETWORK_TYPE        = 19;
    110     protected static final int EVENT_SET_PREFERRED_NETWORK_TYPE        = 20;
    111     protected static final int EVENT_RESET_PREFERRED_NETWORK_TYPE      = 21;
    112     protected static final int EVENT_CHECK_REPORT_GPRS                 = 22;
    113     protected static final int EVENT_RESTRICTED_STATE_CHANGED          = 23;
    114 
    115     /** CDMA events */
    116     protected static final int EVENT_POLL_STATE_REGISTRATION_CDMA      = 24;
    117     protected static final int EVENT_POLL_STATE_OPERATOR_CDMA          = 25;
    118     protected static final int EVENT_RUIM_READY                        = 26;
    119     protected static final int EVENT_RUIM_RECORDS_LOADED               = 27;
    120     protected static final int EVENT_POLL_SIGNAL_STRENGTH_CDMA         = 28;
    121     protected static final int EVENT_GET_SIGNAL_STRENGTH_CDMA          = 29;
    122     protected static final int EVENT_NETWORK_STATE_CHANGED_CDMA        = 30;
    123     protected static final int EVENT_GET_LOC_DONE_CDMA                 = 31;
    124     protected static final int EVENT_SIGNAL_STRENGTH_UPDATE_CDMA       = 32;
    125     protected static final int EVENT_NV_LOADED                         = 33;
    126     protected static final int EVENT_POLL_STATE_CDMA_SUBSCRIPTION      = 34;
    127     protected static final int EVENT_NV_READY                          = 35;
    128     protected static final int EVENT_ERI_FILE_LOADED                   = 36;
    129     protected static final int EVENT_OTA_PROVISION_STATUS_CHANGE       = 37;
    130     protected static final int EVENT_SET_RADIO_POWER_OFF               = 38;
    131     protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED  = 39;
    132     protected static final int EVENT_CDMA_PRL_VERSION_CHANGED          = 40;
    133     protected static final int EVENT_RADIO_ON                          = 41;
    134 
    135 
    136     protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
    137 
    138     /**
    139      * List of ISO codes for countries that can have an offset of
    140      * GMT+0 when not in daylight savings time.  This ignores some
    141      * small places such as the Canary Islands (Spain) and
    142      * Danmarkshavn (Denmark).  The list must be sorted by code.
    143     */
    144     protected static final String[] GMT_COUNTRY_CODES = {
    145         "bf", // Burkina Faso
    146         "ci", // Cote d'Ivoire
    147         "eh", // Western Sahara
    148         "fo", // Faroe Islands, Denmark
    149         "gb", // United Kingdom of Great Britain and Northern Ireland
    150         "gh", // Ghana
    151         "gm", // Gambia
    152         "gn", // Guinea
    153         "gw", // Guinea Bissau
    154         "ie", // Ireland
    155         "lr", // Liberia
    156         "is", // Iceland
    157         "ma", // Morocco
    158         "ml", // Mali
    159         "mr", // Mauritania
    160         "pt", // Portugal
    161         "sl", // Sierra Leone
    162         "sn", // Senegal
    163         "st", // Sao Tome and Principe
    164         "tg", // Togo
    165     };
    166 
    167     /** Reason for registration denial. */
    168     protected static final String REGISTRATION_DENIED_GEN  = "General";
    169     protected static final String REGISTRATION_DENIED_AUTH = "Authentication Failure";
    170 
    171     public ServiceStateTracker() {
    172     }
    173 
    174     public boolean getDesiredPowerState() {
    175         return mDesiredPowerState;
    176     }
    177 
    178     /**
    179      * Registration point for combined roaming on
    180      * combined roaming is true when roaming is true and ONS differs SPN
    181      *
    182      * @param h handler to notify
    183      * @param what what code of message when delivered
    184      * @param obj placed in Message.obj
    185      */
    186     public  void registerForRoamingOn(Handler h, int what, Object obj) {
    187         Registrant r = new Registrant(h, what, obj);
    188         mRoamingOnRegistrants.add(r);
    189 
    190         if (ss.getRoaming()) {
    191             r.notifyRegistrant();
    192         }
    193     }
    194 
    195     public  void unregisterForRoamingOn(Handler h) {
    196         mRoamingOnRegistrants.remove(h);
    197     }
    198 
    199     /**
    200      * Registration point for combined roaming off
    201      * combined roaming is true when roaming is true and ONS differs SPN
    202      *
    203      * @param h handler to notify
    204      * @param what what code of message when delivered
    205      * @param obj placed in Message.obj
    206      */
    207     public  void registerForRoamingOff(Handler h, int what, Object obj) {
    208         Registrant r = new Registrant(h, what, obj);
    209         mRoamingOffRegistrants.add(r);
    210 
    211         if (!ss.getRoaming()) {
    212             r.notifyRegistrant();
    213         }
    214     }
    215 
    216     public  void unregisterForRoamingOff(Handler h) {
    217         mRoamingOffRegistrants.remove(h);
    218     }
    219 
    220     /**
    221      * Re-register network by toggling preferred network type.
    222      * This is a work-around to deregister and register network since there is
    223      * no ril api to set COPS=2 (deregister) only.
    224      *
    225      * @param onComplete is dispatched when this is complete.  it will be
    226      * an AsyncResult, and onComplete.obj.exception will be non-null
    227      * on failure.
    228      */
    229     public void reRegisterNetwork(Message onComplete) {
    230         cm.getPreferredNetworkType(
    231                 obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete));
    232     }
    233 
    234     public void
    235     setRadioPower(boolean power) {
    236         mDesiredPowerState = power;
    237 
    238         setPowerStateToDesired();
    239     }
    240 
    241     /**
    242      * These two flags manage the behavior of the cell lock -- the
    243      * lock should be held if either flag is true.  The intention is
    244      * to allow temporary acquisition of the lock to get a single
    245      * update.  Such a lock grab and release can thus be made to not
    246      * interfere with more permanent lock holds -- in other words, the
    247      * lock will only be released if both flags are false, and so
    248      * releases by temporary users will only affect the lock state if
    249      * there is no continuous user.
    250      */
    251     private boolean mWantContinuousLocationUpdates;
    252     private boolean mWantSingleLocationUpdate;
    253 
    254     public void enableSingleLocationUpdate() {
    255         if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
    256         mWantSingleLocationUpdate = true;
    257         cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
    258     }
    259 
    260     public void enableLocationUpdates() {
    261         if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
    262         mWantContinuousLocationUpdates = true;
    263         cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
    264     }
    265 
    266     protected void disableSingleLocationUpdate() {
    267         mWantSingleLocationUpdate = false;
    268         if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
    269             cm.setLocationUpdates(false, null);
    270         }
    271     }
    272 
    273     public void disableLocationUpdates() {
    274         mWantContinuousLocationUpdates = false;
    275         if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
    276             cm.setLocationUpdates(false, null);
    277         }
    278     }
    279 
    280     @Override
    281     public void handleMessage(Message msg) {
    282         switch (msg.what) {
    283             case EVENT_SET_RADIO_POWER_OFF:
    284                 synchronized(this) {
    285                     if (mPendingRadioPowerOffAfterDataOff &&
    286                             (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) {
    287                         if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
    288                         hangupAndPowerOff();
    289                         mPendingRadioPowerOffAfterDataOffTag += 1;
    290                         mPendingRadioPowerOffAfterDataOff = false;
    291                     } else {
    292                         log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 +
    293                                 "!= tag=" + mPendingRadioPowerOffAfterDataOffTag);
    294                     }
    295                 }
    296                 break;
    297 
    298             default:
    299                 log("Unhandled message with number: " + msg.what);
    300                 break;
    301         }
    302     }
    303 
    304     protected abstract Phone getPhone();
    305     protected abstract void handlePollStateResult(int what, AsyncResult ar);
    306     protected abstract void updateSpnDisplay();
    307     protected abstract void setPowerStateToDesired();
    308     protected abstract void log(String s);
    309     protected abstract void loge(String s);
    310 
    311     public abstract int getCurrentDataConnectionState();
    312     public abstract boolean isConcurrentVoiceAndDataAllowed();
    313 
    314     /**
    315      * Registration point for transition into DataConnection attached.
    316      * @param h handler to notify
    317      * @param what what code of message when delivered
    318      * @param obj placed in Message.obj
    319      */
    320     public void registerForDataConnectionAttached(Handler h, int what, Object obj) {
    321         Registrant r = new Registrant(h, what, obj);
    322         mAttachedRegistrants.add(r);
    323 
    324         if (getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
    325             r.notifyRegistrant();
    326         }
    327     }
    328     public void unregisterForDataConnectionAttached(Handler h) {
    329         mAttachedRegistrants.remove(h);
    330     }
    331 
    332     /**
    333      * Registration point for transition into DataConnection detached.
    334      * @param h handler to notify
    335      * @param what what code of message when delivered
    336      * @param obj placed in Message.obj
    337      */
    338     public void registerForDataConnectionDetached(Handler h, int what, Object obj) {
    339         Registrant r = new Registrant(h, what, obj);
    340         mDetachedRegistrants.add(r);
    341 
    342         if (getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
    343             r.notifyRegistrant();
    344         }
    345     }
    346     public void unregisterForDataConnectionDetached(Handler h) {
    347         mDetachedRegistrants.remove(h);
    348     }
    349 
    350     /**
    351      * Registration point for transition into network attached.
    352      * @param h handler to notify
    353      * @param what what code of message when delivered
    354      * @param obj in Message.obj
    355      */
    356     public void registerForNetworkAttached(Handler h, int what, Object obj) {
    357         Registrant r = new Registrant(h, what, obj);
    358 
    359         mNetworkAttachedRegistrants.add(r);
    360         if (ss.getState() == ServiceState.STATE_IN_SERVICE) {
    361             r.notifyRegistrant();
    362         }
    363     }
    364     public void unregisterForNetworkAttached(Handler h) {
    365         mNetworkAttachedRegistrants.remove(h);
    366     }
    367 
    368     /**
    369      * Registration point for transition into packet service restricted zone.
    370      * @param h handler to notify
    371      * @param what what code of message when delivered
    372      * @param obj placed in Message.obj
    373      */
    374     public void registerForPsRestrictedEnabled(Handler h, int what, Object obj) {
    375         Registrant r = new Registrant(h, what, obj);
    376         mPsRestrictEnabledRegistrants.add(r);
    377 
    378         if (mRestrictedState.isPsRestricted()) {
    379             r.notifyRegistrant();
    380         }
    381     }
    382 
    383     public void unregisterForPsRestrictedEnabled(Handler h) {
    384         mPsRestrictEnabledRegistrants.remove(h);
    385     }
    386 
    387     /**
    388      * Registration point for transition out of packet service restricted zone.
    389      * @param h handler to notify
    390      * @param what what code of message when delivered
    391      * @param obj placed in Message.obj
    392      */
    393     public void registerForPsRestrictedDisabled(Handler h, int what, Object obj) {
    394         Registrant r = new Registrant(h, what, obj);
    395         mPsRestrictDisabledRegistrants.add(r);
    396 
    397         if (mRestrictedState.isPsRestricted()) {
    398             r.notifyRegistrant();
    399         }
    400     }
    401 
    402     public void unregisterForPsRestrictedDisabled(Handler h) {
    403         mPsRestrictDisabledRegistrants.remove(h);
    404     }
    405 
    406     /**
    407      * Clean up existing voice and data connection then turn off radio power.
    408      *
    409      * Hang up the existing voice calls to decrease call drop rate.
    410      */
    411     public void powerOffRadioSafely(DataConnectionTracker dcTracker) {
    412         synchronized (this) {
    413             if (!mPendingRadioPowerOffAfterDataOff) {
    414                 // To minimize race conditions we call cleanUpAllConnections on
    415                 // both if else paths instead of before this isDisconnected test.
    416                 if (dcTracker.isDisconnected()) {
    417                     // To minimize race conditions we do this after isDisconnected
    418                     dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
    419                     if (DBG) log("Data disconnected, turn off radio right away.");
    420                     hangupAndPowerOff();
    421                 } else {
    422                     dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
    423                     Message msg = Message.obtain(this);
    424                     msg.what = EVENT_SET_RADIO_POWER_OFF;
    425                     msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
    426                     if (sendMessageDelayed(msg, 30000)) {
    427                         if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
    428                         mPendingRadioPowerOffAfterDataOff = true;
    429                     } else {
    430                         log("Cannot send delayed Msg, turn off radio right away.");
    431                         hangupAndPowerOff();
    432                     }
    433                 }
    434             }
    435         }
    436     }
    437 
    438     /**
    439      * process the pending request to turn radio off after data is disconnected
    440      *
    441      * return true if there is pending request to process; false otherwise.
    442      */
    443     public boolean processPendingRadioPowerOffAfterDataOff() {
    444         synchronized(this) {
    445             if (mPendingRadioPowerOffAfterDataOff) {
    446                 if (DBG) log("Process pending request to turn radio off.");
    447                 mPendingRadioPowerOffAfterDataOffTag += 1;
    448                 hangupAndPowerOff();
    449                 mPendingRadioPowerOffAfterDataOff = false;
    450                 return true;
    451             }
    452             return false;
    453         }
    454     }
    455 
    456     /**
    457      * Hang up all voice call and turn off radio. Implemented by derived class.
    458      */
    459     protected abstract void hangupAndPowerOff();
    460 
    461     /** Cancel a pending (if any) pollState() operation */
    462     protected void cancelPollState() {
    463         // This will effectively cancel the rest of the poll requests.
    464         pollingContext = new int[1];
    465     }
    466 
    467     /**
    468      * Return true if time zone needs fixing.
    469      *
    470      * @param phoneBase
    471      * @param operatorNumeric
    472      * @param prevOperatorNumeric
    473      * @param needToFixTimeZone
    474      * @return true if time zone needs to be fixed
    475      */
    476     protected boolean shouldFixTimeZoneNow(PhoneBase phoneBase, String operatorNumeric,
    477             String prevOperatorNumeric, boolean needToFixTimeZone) {
    478         // Return false if the mcc isn't valid as we don't know where we are.
    479         // Return true if we have an IccCard and the mcc changed or we
    480         // need to fix it because when the NITZ time came in we didn't
    481         // know the country code.
    482 
    483         // If mcc is invalid then we'll return false
    484         int mcc;
    485         try {
    486             mcc = Integer.parseInt(operatorNumeric.substring(0, 3));
    487         } catch (Exception e) {
    488             if (DBG) {
    489                 log("shouldFixTimeZoneNow: no mcc, operatorNumeric=" + operatorNumeric +
    490                         " retVal=false");
    491             }
    492             return false;
    493         }
    494 
    495         // If prevMcc is invalid will make it different from mcc
    496         // so we'll return true if the card exists.
    497         int prevMcc;
    498         try {
    499             prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3));
    500         } catch (Exception e) {
    501             prevMcc = mcc + 1;
    502         }
    503 
    504         // Determine if the Icc card exists
    505         IccCard iccCard = phoneBase.getIccCard();
    506         boolean iccCardExist = (iccCard != null) && iccCard.getState().iccCardExist();
    507 
    508         // Determine retVal
    509         boolean retVal = ((iccCardExist && (mcc != prevMcc)) || needToFixTimeZone);
    510         if (DBG) {
    511             long ctm = System.currentTimeMillis();
    512             log("shouldFixTimeZoneNow: retVal=" + retVal +
    513                     " iccCard=" + iccCard +
    514                     " iccCard.state=" + (iccCard == null ? "null" : iccCard.getState().toString()) +
    515                     " iccCardExist=" + iccCardExist +
    516                     " operatorNumeric=" + operatorNumeric + " mcc=" + mcc +
    517                     " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc +
    518                     " needToFixTimeZone=" + needToFixTimeZone +
    519                     " ltod=" + TimeUtils.logTimeOfDay(ctm));
    520         }
    521         return retVal;
    522     }
    523 
    524     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    525         pw.println("ServiceStateTracker:");
    526         pw.println(" ss=" + ss);
    527         pw.println(" newSS=" + newSS);
    528         pw.println(" mSignalStrength=" + mSignalStrength);
    529         pw.println(" mRestrictedState=" + mRestrictedState);
    530         pw.println(" pollingContext=" + pollingContext);
    531         pw.println(" mDesiredPowerState=" + mDesiredPowerState);
    532         pw.println(" mRilRadioTechnology=" + mRilRadioTechnology);
    533         pw.println(" mNewRilRadioTechnology=" + mNewRilRadioTechnology);
    534         pw.println(" dontPollSignalStrength=" + dontPollSignalStrength);
    535         pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff);
    536         pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag);
    537     }
    538 }
    539