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