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