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