ServiceStateTracker.java revision b54cd09892e0676d719e6df00e60c77b663f993c
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        mPhoneBase.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
213            ServiceState.rilRadioTechnologyToString(ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN));
214    }
215
216    public void dispose() {
217        mCi.unSetOnSignalStrengthUpdate(this);
218        mUiccController.unregisterForIccChanged(this);
219        mCi.unregisterForCellInfoList(this);
220    }
221
222    public boolean getDesiredPowerState() {
223        return mDesiredPowerState;
224    }
225
226    private SignalStrength mLastSignalStrength = null;
227    protected boolean notifySignalStrength() {
228        boolean notified = false;
229        synchronized(mCellInfo) {
230            if (!mSignalStrength.equals(mLastSignalStrength)) {
231                try {
232                    mPhoneBase.notifySignalStrength();
233                    notified = true;
234                } catch (NullPointerException ex) {
235                    loge("updateSignalStrength() Phone already destroyed: " + ex
236                            + "SignalStrength not notified");
237                }
238            }
239        }
240        return notified;
241    }
242
243    /**
244     * Notify all mDataConnectionRatChangeRegistrants using an
245     * AsyncResult in msg.obj where AsyncResult#result contains the
246     * new RAT as an Integer Object.
247     */
248    protected void notifyDataRegStateRilRadioTechnologyChanged() {
249        int rat = mSS.getRilDataRadioTechnology();
250        int drs = mSS.getDataRegState();
251        if (DBG) log("notifyDataRegStateRilRadioTechnologyChanged: drs=" + drs + " rat=" + rat);
252        mPhoneBase.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
253                ServiceState.rilRadioTechnologyToString(rat));
254        mDataRegStateOrRatChangedRegistrants.notifyResult(new Pair<Integer, Integer>(drs, rat));
255    }
256
257    /**
258     * Some operators have been known to report registration failure
259     * data only devices, to fix that use DataRegState.
260     */
261    protected void useDataRegStateForDataOnlyDevices() {
262        if (mVoiceCapable == false) {
263            if (DBG) {
264                log("useDataRegStateForDataOnlyDevice: VoiceRegState=" + mNewSS.getVoiceRegState()
265                    + " DataRegState=" + mNewSS.getDataRegState());
266            }
267            // TODO: Consider not lying and instead have callers know the difference.
268            mNewSS.setVoiceRegState(mNewSS.getDataRegState());
269        }
270    }
271
272    protected void updatePhoneObject() {
273        mPhoneBase.updatePhoneObject(mSS.getRilVoiceRadioTechnology());
274    }
275
276    /**
277     * Registration point for combined roaming on
278     * combined roaming is true when roaming is true and ONS differs SPN
279     *
280     * @param h handler to notify
281     * @param what what code of message when delivered
282     * @param obj placed in Message.obj
283     */
284    public  void registerForRoamingOn(Handler h, int what, Object obj) {
285        Registrant r = new Registrant(h, what, obj);
286        mRoamingOnRegistrants.add(r);
287
288        if (mSS.getRoaming()) {
289            r.notifyRegistrant();
290        }
291    }
292
293    public  void unregisterForRoamingOn(Handler h) {
294        mRoamingOnRegistrants.remove(h);
295    }
296
297    /**
298     * Registration point for combined roaming off
299     * combined roaming is true when roaming is true and ONS differs SPN
300     *
301     * @param h handler to notify
302     * @param what what code of message when delivered
303     * @param obj placed in Message.obj
304     */
305    public  void registerForRoamingOff(Handler h, int what, Object obj) {
306        Registrant r = new Registrant(h, what, obj);
307        mRoamingOffRegistrants.add(r);
308
309        if (!mSS.getRoaming()) {
310            r.notifyRegistrant();
311        }
312    }
313
314    public  void unregisterForRoamingOff(Handler h) {
315        mRoamingOffRegistrants.remove(h);
316    }
317
318    /**
319     * Re-register network by toggling preferred network type.
320     * This is a work-around to deregister and register network since there is
321     * no ril api to set COPS=2 (deregister) only.
322     *
323     * @param onComplete is dispatched when this is complete.  it will be
324     * an AsyncResult, and onComplete.obj.exception will be non-null
325     * on failure.
326     */
327    public void reRegisterNetwork(Message onComplete) {
328        mCi.getPreferredNetworkType(
329                obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete));
330    }
331
332    public void
333    setRadioPower(boolean power) {
334        mDesiredPowerState = power;
335
336        setPowerStateToDesired();
337    }
338
339    /**
340     * These two flags manage the behavior of the cell lock -- the
341     * lock should be held if either flag is true.  The intention is
342     * to allow temporary acquisition of the lock to get a single
343     * update.  Such a lock grab and release can thus be made to not
344     * interfere with more permanent lock holds -- in other words, the
345     * lock will only be released if both flags are false, and so
346     * releases by temporary users will only affect the lock state if
347     * there is no continuous user.
348     */
349    private boolean mWantContinuousLocationUpdates;
350    private boolean mWantSingleLocationUpdate;
351
352    public void enableSingleLocationUpdate() {
353        if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
354        mWantSingleLocationUpdate = true;
355        mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
356    }
357
358    public void enableLocationUpdates() {
359        if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
360        mWantContinuousLocationUpdates = true;
361        mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
362    }
363
364    protected void disableSingleLocationUpdate() {
365        mWantSingleLocationUpdate = false;
366        if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
367            mCi.setLocationUpdates(false, null);
368        }
369    }
370
371    public void disableLocationUpdates() {
372        mWantContinuousLocationUpdates = false;
373        if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
374            mCi.setLocationUpdates(false, null);
375        }
376    }
377
378    @Override
379    public void handleMessage(Message msg) {
380        switch (msg.what) {
381            case EVENT_SET_RADIO_POWER_OFF:
382                synchronized(this) {
383                    if (mPendingRadioPowerOffAfterDataOff &&
384                            (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) {
385                        if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
386                        hangupAndPowerOff();
387                        mPendingRadioPowerOffAfterDataOffTag += 1;
388                        mPendingRadioPowerOffAfterDataOff = false;
389                    } else {
390                        log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 +
391                                "!= tag=" + mPendingRadioPowerOffAfterDataOffTag);
392                    }
393                }
394                break;
395
396            case EVENT_ICC_CHANGED:
397                onUpdateIccAvailability();
398                break;
399
400            case EVENT_GET_CELL_INFO_LIST: {
401                AsyncResult ar = (AsyncResult) msg.obj;
402                CellInfoResult result = (CellInfoResult) ar.userObj;
403                synchronized(result.lockObj) {
404                    if (ar.exception != null) {
405                        log("EVENT_GET_CELL_INFO_LIST: error ret null, e=" + ar.exception);
406                        result.list = null;
407                    } else {
408                        result.list = (List<CellInfo>) ar.result;
409
410                        if (VDBG) {
411                            log("EVENT_GET_CELL_INFO_LIST: size=" + result.list.size()
412                                    + " list=" + result.list);
413                        }
414                    }
415                    mLastCellInfoListTime = SystemClock.elapsedRealtime();
416                    mLastCellInfoList = result.list;
417                    result.lockObj.notify();
418                }
419                break;
420            }
421
422            case EVENT_UNSOL_CELL_INFO_LIST: {
423                AsyncResult ar = (AsyncResult) msg.obj;
424                if (ar.exception != null) {
425                    log("EVENT_UNSOL_CELL_INFO_LIST: error ignoring, e=" + ar.exception);
426                } else {
427                    List<CellInfo> list = (List<CellInfo>) ar.result;
428                    if (DBG) {
429                        log("EVENT_UNSOL_CELL_INFO_LIST: size=" + list.size()
430                                + " list=" + list);
431                    }
432                    mLastCellInfoListTime = SystemClock.elapsedRealtime();
433                    mLastCellInfoList = list;
434                    mPhoneBase.notifyCellInfo(list);
435                }
436                break;
437            }
438
439            default:
440                log("Unhandled message with number: " + msg.what);
441                break;
442        }
443    }
444
445    protected abstract Phone getPhone();
446    protected abstract void handlePollStateResult(int what, AsyncResult ar);
447    protected abstract void updateSpnDisplay();
448    protected abstract void setPowerStateToDesired();
449    protected abstract void onUpdateIccAvailability();
450    protected abstract void log(String s);
451    protected abstract void loge(String s);
452
453    public abstract int getCurrentDataConnectionState();
454    public abstract boolean isConcurrentVoiceAndDataAllowed();
455
456    /**
457     * Registration point for transition into DataConnection attached.
458     * @param h handler to notify
459     * @param what what code of message when delivered
460     * @param obj placed in Message.obj
461     */
462    public void registerForDataConnectionAttached(Handler h, int what, Object obj) {
463        Registrant r = new Registrant(h, what, obj);
464        mAttachedRegistrants.add(r);
465
466        if (getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
467            r.notifyRegistrant();
468        }
469    }
470    public void unregisterForDataConnectionAttached(Handler h) {
471        mAttachedRegistrants.remove(h);
472    }
473
474    /**
475     * Registration point for transition into DataConnection detached.
476     * @param h handler to notify
477     * @param what what code of message when delivered
478     * @param obj placed in Message.obj
479     */
480    public void registerForDataConnectionDetached(Handler h, int what, Object obj) {
481        Registrant r = new Registrant(h, what, obj);
482        mDetachedRegistrants.add(r);
483
484        if (getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
485            r.notifyRegistrant();
486        }
487    }
488    public void unregisterForDataConnectionDetached(Handler h) {
489        mDetachedRegistrants.remove(h);
490    }
491
492    /**
493     * Registration for DataConnection RIL Data Radio Technology changing. The
494     * new radio technology will be returned AsyncResult#result as an Integer Object.
495     * The AsyncResult will be in the notification Message#obj.
496     *
497     * @param h handler to notify
498     * @param what what code of message when delivered
499     * @param obj placed in Message.obj
500     */
501    public void registerForDataRegStateOrRatChanged(Handler h, int what, Object obj) {
502        Registrant r = new Registrant(h, what, obj);
503        mDataRegStateOrRatChangedRegistrants.add(r);
504        notifyDataRegStateRilRadioTechnologyChanged();
505    }
506    public void unregisterForDataRegStateOrRatChanged(Handler h) {
507        mDataRegStateOrRatChangedRegistrants.remove(h);
508    }
509
510    /**
511     * Registration point for transition into network attached.
512     * @param h handler to notify
513     * @param what what code of message when delivered
514     * @param obj in Message.obj
515     */
516    public void registerForNetworkAttached(Handler h, int what, Object obj) {
517        Registrant r = new Registrant(h, what, obj);
518
519        mNetworkAttachedRegistrants.add(r);
520        if (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
521            r.notifyRegistrant();
522        }
523    }
524    public void unregisterForNetworkAttached(Handler h) {
525        mNetworkAttachedRegistrants.remove(h);
526    }
527
528    /**
529     * Registration point for transition into packet service restricted zone.
530     * @param h handler to notify
531     * @param what what code of message when delivered
532     * @param obj placed in Message.obj
533     */
534    public void registerForPsRestrictedEnabled(Handler h, int what, Object obj) {
535        Registrant r = new Registrant(h, what, obj);
536        mPsRestrictEnabledRegistrants.add(r);
537
538        if (mRestrictedState.isPsRestricted()) {
539            r.notifyRegistrant();
540        }
541    }
542
543    public void unregisterForPsRestrictedEnabled(Handler h) {
544        mPsRestrictEnabledRegistrants.remove(h);
545    }
546
547    /**
548     * Registration point for transition out of packet service restricted zone.
549     * @param h handler to notify
550     * @param what what code of message when delivered
551     * @param obj placed in Message.obj
552     */
553    public void registerForPsRestrictedDisabled(Handler h, int what, Object obj) {
554        Registrant r = new Registrant(h, what, obj);
555        mPsRestrictDisabledRegistrants.add(r);
556
557        if (mRestrictedState.isPsRestricted()) {
558            r.notifyRegistrant();
559        }
560    }
561
562    public void unregisterForPsRestrictedDisabled(Handler h) {
563        mPsRestrictDisabledRegistrants.remove(h);
564    }
565
566    /**
567     * Clean up existing voice and data connection then turn off radio power.
568     *
569     * Hang up the existing voice calls to decrease call drop rate.
570     */
571    public void powerOffRadioSafely(DcTrackerBase dcTracker) {
572        synchronized (this) {
573            if (!mPendingRadioPowerOffAfterDataOff) {
574                // To minimize race conditions we call cleanUpAllConnections on
575                // both if else paths instead of before this isDisconnected test.
576                if (dcTracker.isDisconnected()) {
577                    // To minimize race conditions we do this after isDisconnected
578                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
579                    if (DBG) log("Data disconnected, turn off radio right away.");
580                    hangupAndPowerOff();
581                } else {
582                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
583                    Message msg = Message.obtain(this);
584                    msg.what = EVENT_SET_RADIO_POWER_OFF;
585                    msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
586                    if (sendMessageDelayed(msg, 30000)) {
587                        if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
588                        mPendingRadioPowerOffAfterDataOff = true;
589                    } else {
590                        log("Cannot send delayed Msg, turn off radio right away.");
591                        hangupAndPowerOff();
592                    }
593                }
594            }
595        }
596    }
597
598    /**
599     * process the pending request to turn radio off after data is disconnected
600     *
601     * return true if there is pending request to process; false otherwise.
602     */
603    public boolean processPendingRadioPowerOffAfterDataOff() {
604        synchronized(this) {
605            if (mPendingRadioPowerOffAfterDataOff) {
606                if (DBG) log("Process pending request to turn radio off.");
607                mPendingRadioPowerOffAfterDataOffTag += 1;
608                hangupAndPowerOff();
609                mPendingRadioPowerOffAfterDataOff = false;
610                return true;
611            }
612            return false;
613        }
614    }
615
616    /**
617     * send signal-strength-changed notification if changed Called both for
618     * solicited and unsolicited signal strength updates
619     *
620     * @return true if the signal strength changed and a notification was sent.
621     */
622    protected boolean onSignalStrengthResult(AsyncResult ar, boolean isGsm) {
623        SignalStrength oldSignalStrength = mSignalStrength;
624
625        // This signal is used for both voice and data radio signal so parse
626        // all fields
627
628        if ((ar.exception == null) && (ar.result != null)) {
629            mSignalStrength = (SignalStrength) ar.result;
630            mSignalStrength.validateInput();
631            mSignalStrength.setGsm(isGsm);
632        } else {
633            log("onSignalStrengthResult() Exception from RIL : " + ar.exception);
634            mSignalStrength = new SignalStrength(isGsm);
635        }
636
637        return notifySignalStrength();
638    }
639
640    /**
641     * Hang up all voice call and turn off radio. Implemented by derived class.
642     */
643    protected abstract void hangupAndPowerOff();
644
645    /** Cancel a pending (if any) pollState() operation */
646    protected void cancelPollState() {
647        // This will effectively cancel the rest of the poll requests.
648        mPollingContext = new int[1];
649    }
650
651    /**
652     * Return true if time zone needs fixing.
653     *
654     * @param phoneBase
655     * @param operatorNumeric
656     * @param prevOperatorNumeric
657     * @param needToFixTimeZone
658     * @return true if time zone needs to be fixed
659     */
660    protected boolean shouldFixTimeZoneNow(PhoneBase phoneBase, String operatorNumeric,
661            String prevOperatorNumeric, boolean needToFixTimeZone) {
662        // Return false if the mcc isn't valid as we don't know where we are.
663        // Return true if we have an IccCard and the mcc changed or we
664        // need to fix it because when the NITZ time came in we didn't
665        // know the country code.
666
667        // If mcc is invalid then we'll return false
668        int mcc;
669        try {
670            mcc = Integer.parseInt(operatorNumeric.substring(0, 3));
671        } catch (Exception e) {
672            if (DBG) {
673                log("shouldFixTimeZoneNow: no mcc, operatorNumeric=" + operatorNumeric +
674                        " retVal=false");
675            }
676            return false;
677        }
678
679        // If prevMcc is invalid will make it different from mcc
680        // so we'll return true if the card exists.
681        int prevMcc;
682        try {
683            prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3));
684        } catch (Exception e) {
685            prevMcc = mcc + 1;
686        }
687
688        // Determine if the Icc card exists
689        boolean iccCardExist = false;
690        if (mUiccApplcation != null) {
691            iccCardExist = mUiccApplcation.getState() != AppState.APPSTATE_UNKNOWN;
692        }
693
694        // Determine retVal
695        boolean retVal = ((iccCardExist && (mcc != prevMcc)) || needToFixTimeZone);
696        if (DBG) {
697            long ctm = System.currentTimeMillis();
698            log("shouldFixTimeZoneNow: retVal=" + retVal +
699                    " iccCardExist=" + iccCardExist +
700                    " operatorNumeric=" + operatorNumeric + " mcc=" + mcc +
701                    " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc +
702                    " needToFixTimeZone=" + needToFixTimeZone +
703                    " ltod=" + TimeUtils.logTimeOfDay(ctm));
704        }
705        return retVal;
706    }
707
708    /**
709     * @return all available cell information or null if none.
710     */
711    public List<CellInfo> getAllCellInfo() {
712        CellInfoResult result = new CellInfoResult();
713        if (VDBG) log("SST.getAllCellInfo(): E");
714        int ver = mCi.getRilVersion();
715        if (ver >= 8) {
716            if (isCallerOnDifferentThread()) {
717                if ((SystemClock.elapsedRealtime() - mLastCellInfoListTime)
718                        > LAST_CELL_INFO_LIST_MAX_AGE_MS) {
719                    Message msg = obtainMessage(EVENT_GET_CELL_INFO_LIST, result);
720                    synchronized(result.lockObj) {
721                        mCi.getCellInfoList(msg);
722                        try {
723                            result.lockObj.wait();
724                        } catch (InterruptedException e) {
725                            e.printStackTrace();
726                            result.list = null;
727                        }
728                    }
729                } else {
730                    if (DBG) log("SST.getAllCellInfo(): return last, back to back calls");
731                    result.list = mLastCellInfoList;
732                }
733            } else {
734                if (DBG) log("SST.getAllCellInfo(): return last, same thread can't block");
735                result.list = mLastCellInfoList;
736            }
737        } else {
738            if (DBG) log("SST.getAllCellInfo(): not implemented");
739            result.list = null;
740        }
741        if (DBG) {
742            if (result.list != null) {
743                log("SST.getAllCellInfo(): X size=" + result.list.size()
744                        + " list=" + result.list);
745            } else {
746                log("SST.getAllCellInfo(): X size=0 list=null");
747            }
748        }
749        return result.list;
750    }
751
752    /**
753     * @return signal strength
754     */
755    public SignalStrength getSignalStrength() {
756        synchronized(mCellInfo) {
757            return mSignalStrength;
758        }
759    }
760
761    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
762        pw.println("ServiceStateTracker:");
763        pw.println(" mSS=" + mSS);
764        pw.println(" mNewSS=" + mNewSS);
765        pw.println(" mCellInfo=" + mCellInfo);
766        pw.println(" mRestrictedState=" + mRestrictedState);
767        pw.println(" mPollingContext=" + mPollingContext);
768        pw.println(" mDesiredPowerState=" + mDesiredPowerState);
769        pw.println(" mDontPollSignalStrength=" + mDontPollSignalStrength);
770        pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff);
771        pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag);
772    }
773
774    /**
775     * Verifies the current thread is the same as the thread originally
776     * used in the initialization of this instance. Throws RuntimeException
777     * if not.
778     *
779     * @exception RuntimeException if the current thread is not
780     * the thread that originally obtained this PhoneBase instance.
781     */
782    protected void checkCorrectThread() {
783        if (Thread.currentThread() != getLooper().getThread()) {
784            throw new RuntimeException(
785                    "ServiceStateTracker must be used from within one thread");
786        }
787    }
788
789    protected boolean isCallerOnDifferentThread() {
790        boolean value = Thread.currentThread() != getLooper().getThread();
791        if (VDBG) log("isCallerOnDifferentThread: " + value);
792        return value;
793    }
794}
795