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