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