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