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