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