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