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