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