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