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.content.SharedPreferences;
     23 import android.os.AsyncResult;
     24 import android.os.BaseBundle;
     25 import android.os.Handler;
     26 import android.os.Message;
     27 import android.os.Registrant;
     28 import android.os.RegistrantList;
     29 import android.os.SystemClock;
     30 import android.os.SystemProperties;
     31 import android.preference.PreferenceManager;
     32 import android.telephony.CarrierConfigManager;
     33 import android.telephony.CellInfo;
     34 import android.telephony.Rlog;
     35 import android.telephony.ServiceState;
     36 import android.telephony.SignalStrength;
     37 import android.telephony.SubscriptionManager;
     38 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
     39 import android.telephony.TelephonyManager;
     40 import android.text.TextUtils;
     41 import android.util.Log;
     42 import android.util.Pair;
     43 import android.util.TimeUtils;
     44 import android.net.ConnectivityManager;
     45 import android.net.NetworkInfo;
     46 import android.content.Context;
     47 
     48 import java.io.FileDescriptor;
     49 import java.io.PrintWriter;
     50 import java.util.Arrays;
     51 import java.util.ArrayList;
     52 import java.util.List;
     53 import java.util.concurrent.atomic.AtomicInteger;
     54 
     55 import com.android.internal.telephony.dataconnection.DcTrackerBase;
     56 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
     57 import com.android.internal.telephony.uicc.IccCardProxy;
     58 import com.android.internal.telephony.uicc.IccRecords;
     59 import com.android.internal.telephony.uicc.UiccCardApplication;
     60 import com.android.internal.telephony.uicc.UiccController;
     61 
     62 /**
     63  * {@hide}
     64  */
     65 public abstract class ServiceStateTracker extends Handler {
     66     private static final String LOG_TAG = "SST";
     67     protected  static final boolean DBG = true;
     68     protected static final boolean VDBG = false;
     69 
     70     protected static final String PROP_FORCE_ROAMING = "telephony.test.forceRoaming";
     71 
     72     protected CommandsInterface mCi;
     73     protected UiccController mUiccController = null;
     74     protected UiccCardApplication mUiccApplcation = null;
     75     protected IccRecords mIccRecords = null;
     76 
     77     protected PhoneBase mPhoneBase;
     78 
     79     protected boolean mVoiceCapable;
     80 
     81     public ServiceState mSS = new ServiceState();
     82     protected ServiceState mNewSS = new ServiceState();
     83 
     84     private static final long LAST_CELL_INFO_LIST_MAX_AGE_MS = 2000;
     85     protected long mLastCellInfoListTime;
     86     protected List<CellInfo> mLastCellInfoList = null;
     87 
     88     // This is final as subclasses alias to a more specific type
     89     // so we don't want the reference to change.
     90     protected final CellInfo mCellInfo;
     91 
     92     protected SignalStrength mSignalStrength = new SignalStrength();
     93 
     94     // TODO - this should not be public, right now used externally GsmConnetion.
     95     public RestrictedState mRestrictedState = new RestrictedState();
     96 
     97     /* The otaspMode passed to PhoneStateListener#onOtaspChanged */
     98     static public final int OTASP_UNINITIALIZED = 0;
     99     static public final int OTASP_UNKNOWN = 1;
    100     static public final int OTASP_NEEDED = 2;
    101     static public final int OTASP_NOT_NEEDED = 3;
    102 
    103     /**
    104      * A unique identifier to track requests associated with a poll
    105      * and ignore stale responses.  The value is a count-down of
    106      * expected responses in this pollingContext.
    107      */
    108     protected int[] mPollingContext;
    109     protected boolean mDesiredPowerState;
    110 
    111     /**
    112      * By default, strength polling is enabled.  However, if we're
    113      * getting unsolicited signal strength updates from the radio, set
    114      * value to true and don't bother polling any more.
    115      */
    116     protected boolean mDontPollSignalStrength = false;
    117 
    118     protected RegistrantList mVoiceRoamingOnRegistrants = new RegistrantList();
    119     protected RegistrantList mVoiceRoamingOffRegistrants = new RegistrantList();
    120     protected RegistrantList mDataRoamingOnRegistrants = new RegistrantList();
    121     protected RegistrantList mDataRoamingOffRegistrants = new RegistrantList();
    122     protected RegistrantList mAttachedRegistrants = new RegistrantList();
    123     protected RegistrantList mDetachedRegistrants = new RegistrantList();
    124     protected RegistrantList mDataRegStateOrRatChangedRegistrants = new RegistrantList();
    125     protected RegistrantList mNetworkAttachedRegistrants = new RegistrantList();
    126     protected RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList();
    127     protected RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList();
    128 
    129     /* Radio power off pending flag and tag counter */
    130     protected boolean mPendingRadioPowerOffAfterDataOff = false;
    131     protected int mPendingRadioPowerOffAfterDataOffTag = 0;
    132 
    133     /** Signal strength poll rate. */
    134     protected static final int POLL_PERIOD_MILLIS = 20 * 1000;
    135 
    136     /** Waiting period before recheck gprs and voice registration. */
    137     public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
    138 
    139     /** GSM events */
    140     protected static final int EVENT_RADIO_STATE_CHANGED               = 1;
    141     protected static final int EVENT_NETWORK_STATE_CHANGED             = 2;
    142     protected static final int EVENT_GET_SIGNAL_STRENGTH               = 3;
    143     protected static final int EVENT_POLL_STATE_REGISTRATION           = 4;
    144     protected static final int EVENT_POLL_STATE_GPRS                   = 5;
    145     protected static final int EVENT_POLL_STATE_OPERATOR               = 6;
    146     protected static final int EVENT_POLL_SIGNAL_STRENGTH              = 10;
    147     protected static final int EVENT_NITZ_TIME                         = 11;
    148     protected static final int EVENT_SIGNAL_STRENGTH_UPDATE            = 12;
    149     protected static final int EVENT_RADIO_AVAILABLE                   = 13;
    150     protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14;
    151     protected static final int EVENT_GET_LOC_DONE                      = 15;
    152     protected static final int EVENT_SIM_RECORDS_LOADED                = 16;
    153     protected static final int EVENT_SIM_READY                         = 17;
    154     protected static final int EVENT_LOCATION_UPDATES_ENABLED          = 18;
    155     protected static final int EVENT_GET_PREFERRED_NETWORK_TYPE        = 19;
    156     protected static final int EVENT_SET_PREFERRED_NETWORK_TYPE        = 20;
    157     protected static final int EVENT_RESET_PREFERRED_NETWORK_TYPE      = 21;
    158     protected static final int EVENT_CHECK_REPORT_GPRS                 = 22;
    159     protected static final int EVENT_RESTRICTED_STATE_CHANGED          = 23;
    160 
    161     /** CDMA events */
    162     protected static final int EVENT_POLL_STATE_REGISTRATION_CDMA      = 24;
    163     protected static final int EVENT_POLL_STATE_OPERATOR_CDMA          = 25;
    164     protected static final int EVENT_RUIM_READY                        = 26;
    165     protected static final int EVENT_RUIM_RECORDS_LOADED               = 27;
    166     protected static final int EVENT_POLL_SIGNAL_STRENGTH_CDMA         = 28;
    167     protected static final int EVENT_GET_SIGNAL_STRENGTH_CDMA          = 29;
    168     protected static final int EVENT_NETWORK_STATE_CHANGED_CDMA        = 30;
    169     protected static final int EVENT_GET_LOC_DONE_CDMA                 = 31;
    170     //protected static final int EVENT_UNUSED                            = 32;
    171     protected static final int EVENT_NV_LOADED                         = 33;
    172     protected static final int EVENT_POLL_STATE_CDMA_SUBSCRIPTION      = 34;
    173     protected static final int EVENT_NV_READY                          = 35;
    174     protected static final int EVENT_ERI_FILE_LOADED                   = 36;
    175     protected static final int EVENT_OTA_PROVISION_STATUS_CHANGE       = 37;
    176     protected static final int EVENT_SET_RADIO_POWER_OFF               = 38;
    177     protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED  = 39;
    178     protected static final int EVENT_CDMA_PRL_VERSION_CHANGED          = 40;
    179     protected static final int EVENT_RADIO_ON                          = 41;
    180     public static final int EVENT_ICC_CHANGED                          = 42;
    181     protected static final int EVENT_GET_CELL_INFO_LIST                = 43;
    182     protected static final int EVENT_UNSOL_CELL_INFO_LIST              = 44;
    183     protected static final int EVENT_CHANGE_IMS_STATE                  = 45;
    184     protected static final int EVENT_IMS_STATE_CHANGED                 = 46;
    185     protected static final int EVENT_IMS_STATE_DONE                    = 47;
    186     protected static final int EVENT_IMS_CAPABILITY_CHANGED            = 48;
    187 
    188     protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
    189 
    190     /**
    191      * List of ISO codes for countries that can have an offset of
    192      * GMT+0 when not in daylight savings time.  This ignores some
    193      * small places such as the Canary Islands (Spain) and
    194      * Danmarkshavn (Denmark).  The list must be sorted by code.
    195     */
    196     protected static final String[] GMT_COUNTRY_CODES = {
    197         "bf", // Burkina Faso
    198         "ci", // Cote d'Ivoire
    199         "eh", // Western Sahara
    200         "fo", // Faroe Islands, Denmark
    201         "gb", // United Kingdom of Great Britain and Northern Ireland
    202         "gh", // Ghana
    203         "gm", // Gambia
    204         "gn", // Guinea
    205         "gw", // Guinea Bissau
    206         "ie", // Ireland
    207         "lr", // Liberia
    208         "is", // Iceland
    209         "ma", // Morocco
    210         "ml", // Mali
    211         "mr", // Mauritania
    212         "pt", // Portugal
    213         "sl", // Sierra Leone
    214         "sn", // Senegal
    215         "st", // Sao Tome and Principe
    216         "tg", // Togo
    217     };
    218 
    219     private class CellInfoResult {
    220         List<CellInfo> list;
    221         Object lockObj = new Object();
    222     }
    223 
    224     /** Reason for registration denial. */
    225     protected static final String REGISTRATION_DENIED_GEN  = "General";
    226     protected static final String REGISTRATION_DENIED_AUTH = "Authentication Failure";
    227 
    228     protected boolean mImsRegistrationOnOff = false;
    229     protected boolean mAlarmSwitch = false;
    230     protected IntentFilter mIntentFilter = null;
    231     protected PendingIntent mRadioOffIntent = null;
    232     protected static final String ACTION_RADIO_OFF = "android.intent.action.ACTION_RADIO_OFF";
    233     protected boolean mPowerOffDelayNeed = true;
    234     protected boolean mDeviceShuttingDown = false;
    235     /** Keep track of SPN display rules, so we only broadcast intent if something changes. */
    236     protected boolean mSpnUpdatePending = false;
    237     protected String mCurSpn = null;
    238     protected String mCurDataSpn = null;
    239     protected String mCurPlmn = null;
    240     protected boolean mCurShowPlmn = false;
    241     protected boolean mCurShowSpn = false;
    242     protected int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    243 
    244     private boolean mImsRegistered = false;
    245 
    246     protected SubscriptionManager mSubscriptionManager;
    247     protected SubscriptionController mSubscriptionController;
    248     protected final SstSubscriptionsChangedListener mOnSubscriptionsChangedListener =
    249         new SstSubscriptionsChangedListener();
    250 
    251     protected class SstSubscriptionsChangedListener extends OnSubscriptionsChangedListener {
    252         public final AtomicInteger mPreviousSubId = new AtomicInteger(-1); // < 0 is invalid subId
    253         /**
    254          * Callback invoked when there is any change to any SubscriptionInfo. Typically
    255          * this method would invoke {@link SubscriptionManager#getActiveSubscriptionInfoList}
    256          */
    257         @Override
    258         public void onSubscriptionsChanged() {
    259             if (DBG) log("SubscriptionListener.onSubscriptionInfoChanged");
    260             // Set the network type, in case the radio does not restore it.
    261             int subId = mPhoneBase.getSubId();
    262             if (mPreviousSubId.getAndSet(subId) != subId) {
    263                 if (SubscriptionManager.isValidSubscriptionId(subId)) {
    264                     Context context = mPhoneBase.getContext();
    265 
    266                     mPhoneBase.notifyCallForwardingIndicator();
    267 
    268                     boolean restoreSelection = !context.getResources().getBoolean(
    269                             com.android.internal.R.bool.skip_restoring_network_selection);
    270                     mPhoneBase.sendSubscriptionSettings(restoreSelection);
    271 
    272                     mPhoneBase.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
    273                         ServiceState.rilRadioTechnologyToString(mSS.getRilDataRadioTechnology()));
    274 
    275                     if (mSpnUpdatePending) {
    276                         mSubscriptionController.setPlmnSpn(mPhoneBase.getPhoneId(), mCurShowPlmn,
    277                                 mCurPlmn, mCurShowSpn, mCurSpn);
    278                         mSpnUpdatePending = false;
    279                     }
    280 
    281                     // Remove old network selection sharedPreferences since SP key names are now
    282                     // changed to include subId. This will be done only once when upgrading from an
    283                     // older build that did not include subId in the names.
    284                     SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
    285                             context);
    286                     String oldNetworkSelection = sp.getString(
    287                             PhoneBase.NETWORK_SELECTION_KEY, "");
    288                     String oldNetworkSelectionName = sp.getString(
    289                             PhoneBase.NETWORK_SELECTION_NAME_KEY, "");
    290                     String oldNetworkSelectionShort = sp.getString(
    291                             PhoneBase.NETWORK_SELECTION_SHORT_KEY, "");
    292                     if (!TextUtils.isEmpty(oldNetworkSelection) ||
    293                             !TextUtils.isEmpty(oldNetworkSelectionName) ||
    294                             !TextUtils.isEmpty(oldNetworkSelectionShort)) {
    295                         SharedPreferences.Editor editor = sp.edit();
    296                         editor.putString(PhoneBase.NETWORK_SELECTION_KEY + subId,
    297                                 oldNetworkSelection);
    298                         editor.putString(PhoneBase.NETWORK_SELECTION_NAME_KEY + subId,
    299                                 oldNetworkSelectionName);
    300                         editor.putString(PhoneBase.NETWORK_SELECTION_SHORT_KEY + subId,
    301                                 oldNetworkSelectionShort);
    302                         editor.remove(PhoneBase.NETWORK_SELECTION_KEY);
    303                         editor.remove(PhoneBase.NETWORK_SELECTION_NAME_KEY);
    304                         editor.remove(PhoneBase.NETWORK_SELECTION_SHORT_KEY);
    305                         editor.commit();
    306                     }
    307 
    308                     // Once sub id becomes valid, we need to update the service provider name
    309                     // displayed on the UI again. The old SPN update intents sent to
    310                     // MobileSignalController earlier were actually ignored due to invalid sub id.
    311                     updateSpnDisplay();
    312                 }
    313             }
    314         }
    315     };
    316 
    317     protected ServiceStateTracker(PhoneBase phoneBase, CommandsInterface ci, CellInfo cellInfo) {
    318         mPhoneBase = phoneBase;
    319         mCellInfo = cellInfo;
    320         mCi = ci;
    321         mVoiceCapable = mPhoneBase.getContext().getResources().getBoolean(
    322                 com.android.internal.R.bool.config_voice_capable);
    323         mUiccController = UiccController.getInstance();
    324         mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
    325         mCi.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
    326         mCi.registerForCellInfoList(this, EVENT_UNSOL_CELL_INFO_LIST, null);
    327 
    328         mSubscriptionController = SubscriptionController.getInstance();
    329         mSubscriptionManager = SubscriptionManager.from(phoneBase.getContext());
    330         mSubscriptionManager
    331             .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
    332 
    333         mPhoneBase.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
    334             ServiceState.rilRadioTechnologyToString(ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN));
    335         mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
    336     }
    337 
    338     void requestShutdown() {
    339         if (mDeviceShuttingDown == true) return;
    340         mDeviceShuttingDown = true;
    341         mDesiredPowerState = false;
    342         setPowerStateToDesired();
    343     }
    344 
    345     public void dispose() {
    346         mCi.unSetOnSignalStrengthUpdate(this);
    347         mUiccController.unregisterForIccChanged(this);
    348         mCi.unregisterForCellInfoList(this);
    349         mSubscriptionManager
    350             .removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
    351     }
    352 
    353     public boolean getDesiredPowerState() {
    354         return mDesiredPowerState;
    355     }
    356 
    357     private SignalStrength mLastSignalStrength = null;
    358     protected boolean notifySignalStrength() {
    359         boolean notified = false;
    360         synchronized(mCellInfo) {
    361             if (!mSignalStrength.equals(mLastSignalStrength)) {
    362                 try {
    363                     mPhoneBase.notifySignalStrength();
    364                     notified = true;
    365                 } catch (NullPointerException ex) {
    366                     loge("updateSignalStrength() Phone already destroyed: " + ex
    367                             + "SignalStrength not notified");
    368                 }
    369             }
    370         }
    371         return notified;
    372     }
    373 
    374     /**
    375      * Notify all mDataConnectionRatChangeRegistrants using an
    376      * AsyncResult in msg.obj where AsyncResult#result contains the
    377      * new RAT as an Integer Object.
    378      */
    379     protected void notifyDataRegStateRilRadioTechnologyChanged() {
    380         int rat = mSS.getRilDataRadioTechnology();
    381         int drs = mSS.getDataRegState();
    382         if (DBG) log("notifyDataRegStateRilRadioTechnologyChanged: drs=" + drs + " rat=" + rat);
    383         mPhoneBase.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
    384                 ServiceState.rilRadioTechnologyToString(rat));
    385         mDataRegStateOrRatChangedRegistrants.notifyResult(new Pair<Integer, Integer>(drs, rat));
    386     }
    387 
    388     /**
    389      * Some operators have been known to report registration failure
    390      * data only devices, to fix that use DataRegState.
    391      */
    392     protected void useDataRegStateForDataOnlyDevices() {
    393         if (mVoiceCapable == false) {
    394             if (DBG) {
    395                 log("useDataRegStateForDataOnlyDevice: VoiceRegState=" + mNewSS.getVoiceRegState()
    396                     + " DataRegState=" + mNewSS.getDataRegState());
    397             }
    398             // TODO: Consider not lying and instead have callers know the difference.
    399             mNewSS.setVoiceRegState(mNewSS.getDataRegState());
    400         }
    401     }
    402 
    403     protected void updatePhoneObject() {
    404         if (mPhoneBase.getContext().getResources().
    405                 getBoolean(com.android.internal.R.bool.config_switch_phone_on_voice_reg_state_change)) {
    406             // If the phone is not registered on a network, no need to update.
    407             boolean isRegistered = mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE ||
    408                     mSS.getVoiceRegState() == ServiceState.STATE_EMERGENCY_ONLY;
    409             if (!isRegistered) {
    410                 Rlog.d(LOG_TAG, "updatePhoneObject: Ignore update");
    411                 return;
    412             }
    413             mPhoneBase.updatePhoneObject(mSS.getRilVoiceRadioTechnology());
    414         }
    415     }
    416 
    417     /**
    418      * Registration point for combined roaming on of mobile voice
    419      * combined roaming is true when roaming is true and ONS differs SPN
    420      *
    421      * @param h handler to notify
    422      * @param what what code of message when delivered
    423      * @param obj placed in Message.obj
    424      */
    425     public void registerForVoiceRoamingOn(Handler h, int what, Object obj) {
    426         Registrant r = new Registrant(h, what, obj);
    427         mVoiceRoamingOnRegistrants.add(r);
    428 
    429         if (mSS.getVoiceRoaming()) {
    430             r.notifyRegistrant();
    431         }
    432     }
    433 
    434     public void unregisterForVoiceRoamingOn(Handler h) {
    435         mVoiceRoamingOnRegistrants.remove(h);
    436     }
    437 
    438     /**
    439      * Registration point for roaming off of mobile voice
    440      * combined roaming is true when roaming is true and ONS differs SPN
    441      *
    442      * @param h handler to notify
    443      * @param what what code of message when delivered
    444      * @param obj placed in Message.obj
    445      */
    446     public void registerForVoiceRoamingOff(Handler h, int what, Object obj) {
    447         Registrant r = new Registrant(h, what, obj);
    448         mVoiceRoamingOffRegistrants.add(r);
    449 
    450         if (!mSS.getVoiceRoaming()) {
    451             r.notifyRegistrant();
    452         }
    453     }
    454 
    455     public void unregisterForVoiceRoamingOff(Handler h) {
    456         mVoiceRoamingOffRegistrants.remove(h);
    457     }
    458 
    459     /**
    460      * Registration point for combined roaming on of mobile data
    461      * combined roaming is true when roaming is true and ONS differs SPN
    462      *
    463      * @param h handler to notify
    464      * @param what what code of message when delivered
    465      * @param obj placed in Message.obj
    466      */
    467     public void registerForDataRoamingOn(Handler h, int what, Object obj) {
    468         Registrant r = new Registrant(h, what, obj);
    469         mDataRoamingOnRegistrants.add(r);
    470 
    471         if (mSS.getDataRoaming()) {
    472             r.notifyRegistrant();
    473         }
    474     }
    475 
    476     public void unregisterForDataRoamingOn(Handler h) {
    477         mDataRoamingOnRegistrants.remove(h);
    478     }
    479 
    480     /**
    481      * Registration point for roaming off of mobile data
    482      * combined roaming is true when roaming is true and ONS differs SPN
    483      *
    484      * @param h handler to notify
    485      * @param what what code of message when delivered
    486      * @param obj placed in Message.obj
    487      */
    488     public void registerForDataRoamingOff(Handler h, int what, Object obj) {
    489         Registrant r = new Registrant(h, what, obj);
    490         mDataRoamingOffRegistrants.add(r);
    491 
    492         if (!mSS.getDataRoaming()) {
    493             r.notifyRegistrant();
    494         }
    495     }
    496 
    497     public void unregisterForDataRoamingOff(Handler h) {
    498         mDataRoamingOffRegistrants.remove(h);
    499     }
    500 
    501     /**
    502      * Re-register network by toggling preferred network type.
    503      * This is a work-around to deregister and register network since there is
    504      * no ril api to set COPS=2 (deregister) only.
    505      *
    506      * @param onComplete is dispatched when this is complete.  it will be
    507      * an AsyncResult, and onComplete.obj.exception will be non-null
    508      * on failure.
    509      */
    510     public void reRegisterNetwork(Message onComplete) {
    511         mCi.getPreferredNetworkType(
    512                 obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete));
    513     }
    514 
    515     public void
    516     setRadioPower(boolean power) {
    517         mDesiredPowerState = power;
    518 
    519         setPowerStateToDesired();
    520     }
    521 
    522     /**
    523      * These two flags manage the behavior of the cell lock -- the
    524      * lock should be held if either flag is true.  The intention is
    525      * to allow temporary acquisition of the lock to get a single
    526      * update.  Such a lock grab and release can thus be made to not
    527      * interfere with more permanent lock holds -- in other words, the
    528      * lock will only be released if both flags are false, and so
    529      * releases by temporary users will only affect the lock state if
    530      * there is no continuous user.
    531      */
    532     private boolean mWantContinuousLocationUpdates;
    533     private boolean mWantSingleLocationUpdate;
    534 
    535     public void enableSingleLocationUpdate() {
    536         if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
    537         mWantSingleLocationUpdate = true;
    538         mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
    539     }
    540 
    541     public void enableLocationUpdates() {
    542         if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
    543         mWantContinuousLocationUpdates = true;
    544         mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
    545     }
    546 
    547     protected void disableSingleLocationUpdate() {
    548         mWantSingleLocationUpdate = false;
    549         if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
    550             mCi.setLocationUpdates(false, null);
    551         }
    552     }
    553 
    554     public void disableLocationUpdates() {
    555         mWantContinuousLocationUpdates = false;
    556         if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
    557             mCi.setLocationUpdates(false, null);
    558         }
    559     }
    560 
    561     @Override
    562     public void handleMessage(Message msg) {
    563         switch (msg.what) {
    564             case EVENT_SET_RADIO_POWER_OFF:
    565                 synchronized(this) {
    566                     if (mPendingRadioPowerOffAfterDataOff &&
    567                             (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) {
    568                         if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
    569                         hangupAndPowerOff();
    570                         mPendingRadioPowerOffAfterDataOffTag += 1;
    571                         mPendingRadioPowerOffAfterDataOff = false;
    572                     } else {
    573                         log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 +
    574                                 "!= tag=" + mPendingRadioPowerOffAfterDataOffTag);
    575                     }
    576                 }
    577                 break;
    578 
    579             case EVENT_ICC_CHANGED:
    580                 onUpdateIccAvailability();
    581                 break;
    582 
    583             case EVENT_GET_CELL_INFO_LIST: {
    584                 AsyncResult ar = (AsyncResult) msg.obj;
    585                 CellInfoResult result = (CellInfoResult) ar.userObj;
    586                 synchronized(result.lockObj) {
    587                     if (ar.exception != null) {
    588                         log("EVENT_GET_CELL_INFO_LIST: error ret null, e=" + ar.exception);
    589                         result.list = null;
    590                     } else {
    591                         result.list = (List<CellInfo>) ar.result;
    592 
    593                         if (VDBG) {
    594                             log("EVENT_GET_CELL_INFO_LIST: size=" + result.list.size()
    595                                     + " list=" + result.list);
    596                         }
    597                     }
    598                     mLastCellInfoListTime = SystemClock.elapsedRealtime();
    599                     mLastCellInfoList = result.list;
    600                     result.lockObj.notify();
    601                 }
    602                 break;
    603             }
    604 
    605             case EVENT_UNSOL_CELL_INFO_LIST: {
    606                 AsyncResult ar = (AsyncResult) msg.obj;
    607                 if (ar.exception != null) {
    608                     log("EVENT_UNSOL_CELL_INFO_LIST: error ignoring, e=" + ar.exception);
    609                 } else {
    610                     List<CellInfo> list = (List<CellInfo>) ar.result;
    611                     if (DBG) {
    612                         log("EVENT_UNSOL_CELL_INFO_LIST: size=" + list.size()
    613                                 + " list=" + list);
    614                     }
    615                     mLastCellInfoListTime = SystemClock.elapsedRealtime();
    616                     mLastCellInfoList = list;
    617                     mPhoneBase.notifyCellInfo(list);
    618                 }
    619                 break;
    620             }
    621 
    622             case  EVENT_IMS_STATE_CHANGED: // received unsol
    623                 mCi.getImsRegistrationState(this.obtainMessage(EVENT_IMS_STATE_DONE));
    624                 break;
    625 
    626             case EVENT_IMS_STATE_DONE:
    627                 AsyncResult ar = (AsyncResult) msg.obj;
    628                 if (ar.exception == null) {
    629                     int[] responseArray = (int[])ar.result;
    630                     mImsRegistered = (responseArray[0] == 1) ? true : false;
    631                 }
    632                 break;
    633 
    634             default:
    635                 log("Unhandled message with number: " + msg.what);
    636                 break;
    637         }
    638     }
    639 
    640     protected abstract Phone getPhone();
    641     protected abstract void handlePollStateResult(int what, AsyncResult ar);
    642     protected abstract void updateSpnDisplay();
    643     protected abstract void setPowerStateToDesired();
    644     protected abstract void onUpdateIccAvailability();
    645     protected abstract void log(String s);
    646     protected abstract void loge(String s);
    647 
    648     public abstract int getCurrentDataConnectionState();
    649     public abstract boolean isConcurrentVoiceAndDataAllowed();
    650 
    651     public abstract void setImsRegistrationState(boolean registered);
    652     public void onImsCapabilityChanged() {}
    653     public abstract void pollState();
    654 
    655     /**
    656      * Registration point for transition into DataConnection attached.
    657      * @param h handler to notify
    658      * @param what what code of message when delivered
    659      * @param obj placed in Message.obj
    660      */
    661     public void registerForDataConnectionAttached(Handler h, int what, Object obj) {
    662         Registrant r = new Registrant(h, what, obj);
    663         mAttachedRegistrants.add(r);
    664 
    665         if (getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
    666             r.notifyRegistrant();
    667         }
    668     }
    669     public void unregisterForDataConnectionAttached(Handler h) {
    670         mAttachedRegistrants.remove(h);
    671     }
    672 
    673     /**
    674      * Registration point for transition into DataConnection detached.
    675      * @param h handler to notify
    676      * @param what what code of message when delivered
    677      * @param obj placed in Message.obj
    678      */
    679     public void registerForDataConnectionDetached(Handler h, int what, Object obj) {
    680         Registrant r = new Registrant(h, what, obj);
    681         mDetachedRegistrants.add(r);
    682 
    683         if (getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
    684             r.notifyRegistrant();
    685         }
    686     }
    687     public void unregisterForDataConnectionDetached(Handler h) {
    688         mDetachedRegistrants.remove(h);
    689     }
    690 
    691     /**
    692      * Registration for DataConnection RIL Data Radio Technology changing. The
    693      * new radio technology will be returned AsyncResult#result as an Integer Object.
    694      * The AsyncResult will be in the notification Message#obj.
    695      *
    696      * @param h handler to notify
    697      * @param what what code of message when delivered
    698      * @param obj placed in Message.obj
    699      */
    700     public void registerForDataRegStateOrRatChanged(Handler h, int what, Object obj) {
    701         Registrant r = new Registrant(h, what, obj);
    702         mDataRegStateOrRatChangedRegistrants.add(r);
    703         notifyDataRegStateRilRadioTechnologyChanged();
    704     }
    705     public void unregisterForDataRegStateOrRatChanged(Handler h) {
    706         mDataRegStateOrRatChangedRegistrants.remove(h);
    707     }
    708 
    709     /**
    710      * Registration point for transition into network attached.
    711      * @param h handler to notify
    712      * @param what what code of message when delivered
    713      * @param obj in Message.obj
    714      */
    715     public void registerForNetworkAttached(Handler h, int what, Object obj) {
    716         Registrant r = new Registrant(h, what, obj);
    717 
    718         mNetworkAttachedRegistrants.add(r);
    719         if (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
    720             r.notifyRegistrant();
    721         }
    722     }
    723     public void unregisterForNetworkAttached(Handler h) {
    724         mNetworkAttachedRegistrants.remove(h);
    725     }
    726 
    727     /**
    728      * Registration point for transition into packet service restricted zone.
    729      * @param h handler to notify
    730      * @param what what code of message when delivered
    731      * @param obj placed in Message.obj
    732      */
    733     public void registerForPsRestrictedEnabled(Handler h, int what, Object obj) {
    734         Registrant r = new Registrant(h, what, obj);
    735         mPsRestrictEnabledRegistrants.add(r);
    736 
    737         if (mRestrictedState.isPsRestricted()) {
    738             r.notifyRegistrant();
    739         }
    740     }
    741 
    742     public void unregisterForPsRestrictedEnabled(Handler h) {
    743         mPsRestrictEnabledRegistrants.remove(h);
    744     }
    745 
    746     /**
    747      * Registration point for transition out of packet service restricted zone.
    748      * @param h handler to notify
    749      * @param what what code of message when delivered
    750      * @param obj placed in Message.obj
    751      */
    752     public void registerForPsRestrictedDisabled(Handler h, int what, Object obj) {
    753         Registrant r = new Registrant(h, what, obj);
    754         mPsRestrictDisabledRegistrants.add(r);
    755 
    756         if (mRestrictedState.isPsRestricted()) {
    757             r.notifyRegistrant();
    758         }
    759     }
    760 
    761     public void unregisterForPsRestrictedDisabled(Handler h) {
    762         mPsRestrictDisabledRegistrants.remove(h);
    763     }
    764 
    765     /**
    766      * Clean up existing voice and data connection then turn off radio power.
    767      *
    768      * Hang up the existing voice calls to decrease call drop rate.
    769      */
    770     public void powerOffRadioSafely(DcTrackerBase dcTracker) {
    771         synchronized (this) {
    772             if (!mPendingRadioPowerOffAfterDataOff) {
    773                 // In some network, deactivate PDP connection cause releasing of RRC connection,
    774                 // which MM/IMSI detaching request needs. Without this detaching, network can
    775                 // not release the network resources previously attached.
    776                 // So we are avoiding data detaching on these networks.
    777                 String[] networkNotClearData = mPhoneBase.getContext().getResources()
    778                         .getStringArray(com.android.internal.R.array.networks_not_clear_data);
    779                 String currentNetwork = mSS.getOperatorNumeric();
    780                 if ((networkNotClearData != null) && (currentNetwork != null)) {
    781                     for (int i = 0; i < networkNotClearData.length; i++) {
    782                         if (currentNetwork.equals(networkNotClearData[i])) {
    783                             // Don't clear data connection for this carrier
    784                             if (DBG)
    785                                 log("Not disconnecting data for " + currentNetwork);
    786                             hangupAndPowerOff();
    787                             return;
    788                         }
    789                     }
    790                 }
    791                 // To minimize race conditions we call cleanUpAllConnections on
    792                 // both if else paths instead of before this isDisconnected test.
    793                 if (dcTracker.isDisconnected()) {
    794                     // To minimize race conditions we do this after isDisconnected
    795                     dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
    796                     if (DBG) log("Data disconnected, turn off radio right away.");
    797                     hangupAndPowerOff();
    798                 } else {
    799                     dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
    800                     Message msg = Message.obtain(this);
    801                     msg.what = EVENT_SET_RADIO_POWER_OFF;
    802                     msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
    803                     if (sendMessageDelayed(msg, 30000)) {
    804                         if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
    805                         mPendingRadioPowerOffAfterDataOff = true;
    806                     } else {
    807                         log("Cannot send delayed Msg, turn off radio right away.");
    808                         hangupAndPowerOff();
    809                     }
    810                 }
    811             }
    812         }
    813     }
    814 
    815     /**
    816      * process the pending request to turn radio off after data is disconnected
    817      *
    818      * return true if there is pending request to process; false otherwise.
    819      */
    820     public boolean processPendingRadioPowerOffAfterDataOff() {
    821         synchronized(this) {
    822             if (mPendingRadioPowerOffAfterDataOff) {
    823                 if (DBG) log("Process pending request to turn radio off.");
    824                 mPendingRadioPowerOffAfterDataOffTag += 1;
    825                 hangupAndPowerOff();
    826                 mPendingRadioPowerOffAfterDataOff = false;
    827                 return true;
    828             }
    829             return false;
    830         }
    831     }
    832 
    833     /**
    834      * send signal-strength-changed notification if changed Called both for
    835      * solicited and unsolicited signal strength updates
    836      *
    837      * @return true if the signal strength changed and a notification was sent.
    838      */
    839     protected boolean onSignalStrengthResult(AsyncResult ar, boolean isGsm) {
    840         SignalStrength oldSignalStrength = mSignalStrength;
    841 
    842         // This signal is used for both voice and data radio signal so parse
    843         // all fields
    844 
    845         if ((ar.exception == null) && (ar.result != null)) {
    846             mSignalStrength = (SignalStrength) ar.result;
    847             mSignalStrength.validateInput();
    848             mSignalStrength.setGsm(isGsm);
    849         } else {
    850             log("onSignalStrengthResult() Exception from RIL : " + ar.exception);
    851             mSignalStrength = new SignalStrength(isGsm);
    852         }
    853 
    854         return notifySignalStrength();
    855     }
    856 
    857     /**
    858      * Hang up all voice call and turn off radio. Implemented by derived class.
    859      */
    860     protected abstract void hangupAndPowerOff();
    861 
    862     /** Cancel a pending (if any) pollState() operation */
    863     protected void cancelPollState() {
    864         // This will effectively cancel the rest of the poll requests.
    865         mPollingContext = new int[1];
    866     }
    867 
    868     /**
    869      * Return true if time zone needs fixing.
    870      *
    871      * @param phoneBase
    872      * @param operatorNumeric
    873      * @param prevOperatorNumeric
    874      * @param needToFixTimeZone
    875      * @return true if time zone needs to be fixed
    876      */
    877     protected boolean shouldFixTimeZoneNow(PhoneBase phoneBase, String operatorNumeric,
    878             String prevOperatorNumeric, boolean needToFixTimeZone) {
    879         // Return false if the mcc isn't valid as we don't know where we are.
    880         // Return true if we have an IccCard and the mcc changed or we
    881         // need to fix it because when the NITZ time came in we didn't
    882         // know the country code.
    883 
    884         // If mcc is invalid then we'll return false
    885         int mcc;
    886         try {
    887             mcc = Integer.parseInt(operatorNumeric.substring(0, 3));
    888         } catch (Exception e) {
    889             if (DBG) {
    890                 log("shouldFixTimeZoneNow: no mcc, operatorNumeric=" + operatorNumeric +
    891                         " retVal=false");
    892             }
    893             return false;
    894         }
    895 
    896         // If prevMcc is invalid will make it different from mcc
    897         // so we'll return true if the card exists.
    898         int prevMcc;
    899         try {
    900             prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3));
    901         } catch (Exception e) {
    902             prevMcc = mcc + 1;
    903         }
    904 
    905         // Determine if the Icc card exists
    906         boolean iccCardExist = false;
    907         if (mUiccApplcation != null) {
    908             iccCardExist = mUiccApplcation.getState() != AppState.APPSTATE_UNKNOWN;
    909         }
    910 
    911         // Determine retVal
    912         boolean retVal = ((iccCardExist && (mcc != prevMcc)) || needToFixTimeZone);
    913         if (DBG) {
    914             long ctm = System.currentTimeMillis();
    915             log("shouldFixTimeZoneNow: retVal=" + retVal +
    916                     " iccCardExist=" + iccCardExist +
    917                     " operatorNumeric=" + operatorNumeric + " mcc=" + mcc +
    918                     " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc +
    919                     " needToFixTimeZone=" + needToFixTimeZone +
    920                     " ltod=" + TimeUtils.logTimeOfDay(ctm));
    921         }
    922         return retVal;
    923     }
    924 
    925     public String getSystemProperty(String property, String defValue) {
    926         return TelephonyManager.getTelephonyProperty(mPhoneBase.getPhoneId(), property, defValue);
    927     }
    928 
    929     /**
    930      * @return all available cell information or null if none.
    931      */
    932     public List<CellInfo> getAllCellInfo() {
    933         CellInfoResult result = new CellInfoResult();
    934         if (VDBG) log("SST.getAllCellInfo(): E");
    935         int ver = mCi.getRilVersion();
    936         if (ver >= 8) {
    937             if (isCallerOnDifferentThread()) {
    938                 if ((SystemClock.elapsedRealtime() - mLastCellInfoListTime)
    939                         > LAST_CELL_INFO_LIST_MAX_AGE_MS) {
    940                     Message msg = obtainMessage(EVENT_GET_CELL_INFO_LIST, result);
    941                     synchronized(result.lockObj) {
    942                         result.list = null;
    943                         mCi.getCellInfoList(msg);
    944                         try {
    945                             result.lockObj.wait(5000);
    946                         } catch (InterruptedException e) {
    947                             e.printStackTrace();
    948                         }
    949                     }
    950                 } else {
    951                     if (DBG) log("SST.getAllCellInfo(): return last, back to back calls");
    952                     result.list = mLastCellInfoList;
    953                 }
    954             } else {
    955                 if (DBG) log("SST.getAllCellInfo(): return last, same thread can't block");
    956                 result.list = mLastCellInfoList;
    957             }
    958         } else {
    959             if (DBG) log("SST.getAllCellInfo(): not implemented");
    960             result.list = null;
    961         }
    962         synchronized(result.lockObj) {
    963             if (result.list != null) {
    964                 if (DBG) log("SST.getAllCellInfo(): X size=" + result.list.size()
    965                         + " list=" + result.list);
    966                 return result.list;
    967             } else {
    968                 if (DBG) log("SST.getAllCellInfo(): X size=0 list=null");
    969                 return null;
    970             }
    971         }
    972     }
    973 
    974     /**
    975      * @return signal strength
    976      */
    977     public SignalStrength getSignalStrength() {
    978         synchronized(mCellInfo) {
    979             return mSignalStrength;
    980         }
    981     }
    982 
    983     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    984         pw.println("ServiceStateTracker:");
    985         pw.println(" mSS=" + mSS);
    986         pw.println(" mNewSS=" + mNewSS);
    987         pw.println(" mCellInfo=" + mCellInfo);
    988         pw.println(" mRestrictedState=" + mRestrictedState);
    989         pw.println(" mPollingContext=" + mPollingContext);
    990         pw.println(" mDesiredPowerState=" + mDesiredPowerState);
    991         pw.println(" mDontPollSignalStrength=" + mDontPollSignalStrength);
    992         pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff);
    993         pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag);
    994         pw.flush();
    995     }
    996 
    997     public boolean isImsRegistered() {
    998         return mImsRegistered;
    999     }
   1000     /**
   1001      * Verifies the current thread is the same as the thread originally
   1002      * used in the initialization of this instance. Throws RuntimeException
   1003      * if not.
   1004      *
   1005      * @exception RuntimeException if the current thread is not
   1006      * the thread that originally obtained this PhoneBase instance.
   1007      */
   1008     protected void checkCorrectThread() {
   1009         if (Thread.currentThread() != getLooper().getThread()) {
   1010             throw new RuntimeException(
   1011                     "ServiceStateTracker must be used from within one thread");
   1012         }
   1013     }
   1014 
   1015     protected boolean isCallerOnDifferentThread() {
   1016         boolean value = Thread.currentThread() != getLooper().getThread();
   1017         if (VDBG) log("isCallerOnDifferentThread: " + value);
   1018         return value;
   1019     }
   1020 
   1021     protected void updateCarrierMccMncConfiguration(String newOp, String oldOp, Context context) {
   1022         // if we have a change in operator, notify wifi (even to/from none)
   1023         if (((newOp == null) && (TextUtils.isEmpty(oldOp) == false)) ||
   1024                 ((newOp != null) && (newOp.equals(oldOp) == false))) {
   1025             log("update mccmnc=" + newOp + " fromServiceState=true");
   1026             MccTable.updateMccMncConfiguration(context, newOp, true);
   1027         }
   1028     }
   1029 
   1030     /**
   1031      * Check ISO country by MCC to see if phone is roaming in same registered country
   1032      */
   1033     protected boolean inSameCountry(String operatorNumeric) {
   1034         if (TextUtils.isEmpty(operatorNumeric) || (operatorNumeric.length() < 5)) {
   1035             // Not a valid network
   1036             return false;
   1037         }
   1038         final String homeNumeric = getHomeOperatorNumeric();
   1039         if (TextUtils.isEmpty(homeNumeric) || (homeNumeric.length() < 5)) {
   1040             // Not a valid SIM MCC
   1041             return false;
   1042         }
   1043         boolean inSameCountry = true;
   1044         final String networkMCC = operatorNumeric.substring(0, 3);
   1045         final String homeMCC = homeNumeric.substring(0, 3);
   1046         final String networkCountry = MccTable.countryCodeForMcc(Integer.parseInt(networkMCC));
   1047         final String homeCountry = MccTable.countryCodeForMcc(Integer.parseInt(homeMCC));
   1048         if (networkCountry.isEmpty() || homeCountry.isEmpty()) {
   1049             // Not a valid country
   1050             return false;
   1051         }
   1052         inSameCountry = homeCountry.equals(networkCountry);
   1053         if (inSameCountry) {
   1054             return inSameCountry;
   1055         }
   1056         // special same country cases
   1057         if ("us".equals(homeCountry) && "vi".equals(networkCountry)) {
   1058             inSameCountry = true;
   1059         } else if ("vi".equals(homeCountry) && "us".equals(networkCountry)) {
   1060             inSameCountry = true;
   1061         }
   1062         return inSameCountry;
   1063     }
   1064 
   1065     protected abstract void setRoamingType(ServiceState currentServiceState);
   1066 
   1067     protected String getHomeOperatorNumeric() {
   1068         return ((TelephonyManager) mPhoneBase.getContext().
   1069                 getSystemService(Context.TELEPHONY_SERVICE)).
   1070                 getSimOperatorNumericForPhone(mPhoneBase.getPhoneId());
   1071     }
   1072 
   1073     protected int getPhoneId() {
   1074         return mPhoneBase.getPhoneId();
   1075     }
   1076 
   1077     /* Reset Service state when IWLAN is enabled as polling in airplane mode
   1078      * causes state to go to OUT_OF_SERVICE state instead of STATE_OFF
   1079      */
   1080     protected void resetServiceStateInIwlanMode() {
   1081         if (mCi.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
   1082             boolean resetIwlanRatVal = false;
   1083             log("set service state as POWER_OFF");
   1084             if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
   1085                         == mNewSS.getRilDataRadioTechnology()) {
   1086                 log("pollStateDone: mNewSS = " + mNewSS);
   1087                 log("pollStateDone: reset iwlan RAT value");
   1088                 resetIwlanRatVal = true;
   1089             }
   1090             mNewSS.setStateOff();
   1091             if (resetIwlanRatVal) {
   1092                 mNewSS.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN);
   1093                 mNewSS.setDataRegState(ServiceState.STATE_IN_SERVICE);
   1094                 log("pollStateDone: mNewSS = " + mNewSS);
   1095             }
   1096         }
   1097     }
   1098 
   1099     /**
   1100      * Check if device is non-roaming and always on home network.
   1101      *
   1102      * @param b carrier config bundle obtained from CarrierConfigManager
   1103      * @return true if network is always on home network, false otherwise
   1104      * @see CarrierConfigManager
   1105      */
   1106     protected final boolean alwaysOnHomeNetwork(BaseBundle b) {
   1107         return b.getBoolean(CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL);
   1108     }
   1109 
   1110     /**
   1111      * Check if the network identifier has membership in the set of
   1112      * network identifiers stored in the carrier config bundle.
   1113      *
   1114      * @param b carrier config bundle obtained from CarrierConfigManager
   1115      * @param network The network identifier to check network existence in bundle
   1116      * @param key The key to index into the bundle presenting a string array of
   1117      *            networks to check membership
   1118      * @return true if network has membership in bundle networks, false otherwise
   1119      * @see CarrierConfigManager
   1120      */
   1121     private boolean isInNetwork(BaseBundle b, String network, String key) {
   1122         String[] networks = b.getStringArray(key);
   1123 
   1124         if (networks != null && Arrays.asList(networks).contains(network)) {
   1125             return true;
   1126         }
   1127         return false;
   1128     }
   1129 
   1130     protected final boolean isRoamingInGsmNetwork(BaseBundle b, String network) {
   1131         return isInNetwork(b, network, CarrierConfigManager.KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY);
   1132     }
   1133 
   1134     protected final boolean isNonRoamingInGsmNetwork(BaseBundle b, String network) {
   1135         return isInNetwork(b, network, CarrierConfigManager.KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY);
   1136     }
   1137 
   1138     protected final boolean isRoamingInCdmaNetwork(BaseBundle b, String network) {
   1139         return isInNetwork(b, network, CarrierConfigManager.KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY);
   1140     }
   1141 
   1142     protected final boolean isNonRoamingInCdmaNetwork(BaseBundle b, String network) {
   1143         return isInNetwork(b, network, CarrierConfigManager.KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY);
   1144     }
   1145 }
   1146