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
onSubscriptionsChanged()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 
ServiceStateTracker(PhoneBase phoneBase, CommandsInterface ci, CellInfo cellInfo)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 
requestShutdown()338     void requestShutdown() {
339         if (mDeviceShuttingDown == true) return;
340         mDeviceShuttingDown = true;
341         mDesiredPowerState = false;
342         setPowerStateToDesired();
343     }
344 
dispose()345     public void dispose() {
346         mCi.unSetOnSignalStrengthUpdate(this);
347         mUiccController.unregisterForIccChanged(this);
348         mCi.unregisterForCellInfoList(this);
349         mSubscriptionManager
350             .removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
351     }
352 
getDesiredPowerState()353     public boolean getDesiredPowerState() {
354         return mDesiredPowerState;
355     }
356 
357     private SignalStrength mLastSignalStrength = null;
notifySignalStrength()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      */
notifyDataRegStateRilRadioTechnologyChanged()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      */
useDataRegStateForDataOnlyDevices()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 
updatePhoneObject()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      */
registerForVoiceRoamingOn(Handler h, int what, Object obj)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 
unregisterForVoiceRoamingOn(Handler h)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      */
registerForVoiceRoamingOff(Handler h, int what, Object obj)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 
unregisterForVoiceRoamingOff(Handler h)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      */
registerForDataRoamingOn(Handler h, int what, Object obj)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 
unregisterForDataRoamingOn(Handler h)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      */
registerForDataRoamingOff(Handler h, int what, Object obj)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 
unregisterForDataRoamingOff(Handler h)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      */
reRegisterNetwork(Message onComplete)510     public void reRegisterNetwork(Message onComplete) {
511         mCi.getPreferredNetworkType(
512                 obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete));
513     }
514 
515     public void
setRadioPower(boolean power)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 
enableSingleLocationUpdate()535     public void enableSingleLocationUpdate() {
536         if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
537         mWantSingleLocationUpdate = true;
538         mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
539     }
540 
enableLocationUpdates()541     public void enableLocationUpdates() {
542         if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
543         mWantContinuousLocationUpdates = true;
544         mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
545     }
546 
disableSingleLocationUpdate()547     protected void disableSingleLocationUpdate() {
548         mWantSingleLocationUpdate = false;
549         if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
550             mCi.setLocationUpdates(false, null);
551         }
552     }
553 
disableLocationUpdates()554     public void disableLocationUpdates() {
555         mWantContinuousLocationUpdates = false;
556         if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
557             mCi.setLocationUpdates(false, null);
558         }
559     }
560 
561     @Override
handleMessage(Message msg)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 
getPhone()640     protected abstract Phone getPhone();
handlePollStateResult(int what, AsyncResult ar)641     protected abstract void handlePollStateResult(int what, AsyncResult ar);
updateSpnDisplay()642     protected abstract void updateSpnDisplay();
setPowerStateToDesired()643     protected abstract void setPowerStateToDesired();
onUpdateIccAvailability()644     protected abstract void onUpdateIccAvailability();
log(String s)645     protected abstract void log(String s);
loge(String s)646     protected abstract void loge(String s);
647 
getCurrentDataConnectionState()648     public abstract int getCurrentDataConnectionState();
isConcurrentVoiceAndDataAllowed()649     public abstract boolean isConcurrentVoiceAndDataAllowed();
650 
setImsRegistrationState(boolean registered)651     public abstract void setImsRegistrationState(boolean registered);
onImsCapabilityChanged()652     public void onImsCapabilityChanged() {}
pollState()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      */
registerForDataConnectionAttached(Handler h, int what, Object obj)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     }
unregisterForDataConnectionAttached(Handler h)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      */
registerForDataConnectionDetached(Handler h, int what, Object obj)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     }
unregisterForDataConnectionDetached(Handler h)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      */
registerForDataRegStateOrRatChanged(Handler h, int what, Object obj)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     }
unregisterForDataRegStateOrRatChanged(Handler h)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      */
registerForNetworkAttached(Handler h, int what, Object obj)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     }
unregisterForNetworkAttached(Handler h)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      */
registerForPsRestrictedEnabled(Handler h, int what, Object obj)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 
unregisterForPsRestrictedEnabled(Handler h)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      */
registerForPsRestrictedDisabled(Handler h, int what, Object obj)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 
unregisterForPsRestrictedDisabled(Handler h)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      */
powerOffRadioSafely(DcTrackerBase dcTracker)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      */
processPendingRadioPowerOffAfterDataOff()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      */
onSignalStrengthResult(AsyncResult ar, boolean isGsm)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      */
hangupAndPowerOff()860     protected abstract void hangupAndPowerOff();
861 
862     /** Cancel a pending (if any) pollState() operation */
cancelPollState()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      */
shouldFixTimeZoneNow(PhoneBase phoneBase, String operatorNumeric, String prevOperatorNumeric, boolean needToFixTimeZone)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 
getSystemProperty(String property, String defValue)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      */
getAllCellInfo()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      */
getSignalStrength()977     public SignalStrength getSignalStrength() {
978         synchronized(mCellInfo) {
979             return mSignalStrength;
980         }
981     }
982 
dump(FileDescriptor fd, PrintWriter pw, String[] args)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 
isImsRegistered()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      */
checkCorrectThread()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 
isCallerOnDifferentThread()1015     protected boolean isCallerOnDifferentThread() {
1016         boolean value = Thread.currentThread() != getLooper().getThread();
1017         if (VDBG) log("isCallerOnDifferentThread: " + value);
1018         return value;
1019     }
1020 
updateCarrierMccMncConfiguration(String newOp, String oldOp, Context context)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      */
inSameCountry(String operatorNumeric)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 
setRoamingType(ServiceState currentServiceState)1065     protected abstract void setRoamingType(ServiceState currentServiceState);
1066 
getHomeOperatorNumeric()1067     protected String getHomeOperatorNumeric() {
1068         return ((TelephonyManager) mPhoneBase.getContext().
1069                 getSystemService(Context.TELEPHONY_SERVICE)).
1070                 getSimOperatorNumericForPhone(mPhoneBase.getPhoneId());
1071     }
1072 
getPhoneId()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      */
resetServiceStateInIwlanMode()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      */
alwaysOnHomeNetwork(BaseBundle b)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      */
isInNetwork(BaseBundle b, String network, String key)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 
isRoamingInGsmNetwork(BaseBundle b, String network)1130     protected final boolean isRoamingInGsmNetwork(BaseBundle b, String network) {
1131         return isInNetwork(b, network, CarrierConfigManager.KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY);
1132     }
1133 
isNonRoamingInGsmNetwork(BaseBundle b, String network)1134     protected final boolean isNonRoamingInGsmNetwork(BaseBundle b, String network) {
1135         return isInNetwork(b, network, CarrierConfigManager.KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY);
1136     }
1137 
isRoamingInCdmaNetwork(BaseBundle b, String network)1138     protected final boolean isRoamingInCdmaNetwork(BaseBundle b, String network) {
1139         return isInNetwork(b, network, CarrierConfigManager.KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY);
1140     }
1141 
isNonRoamingInCdmaNetwork(BaseBundle b, String network)1142     protected final boolean isNonRoamingInCdmaNetwork(BaseBundle b, String network) {
1143         return isInNetwork(b, network, CarrierConfigManager.KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY);
1144     }
1145 }
1146