ServiceStateTracker.java revision ef1d4bff9bbf7d967dbcace73f08910e14e367d0
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.os.AsyncResult;
20import android.os.Handler;
21import android.os.Message;
22import android.os.Registrant;
23import android.os.RegistrantList;
24import android.os.SystemClock;
25import android.telephony.CellInfo;
26import android.telephony.ServiceState;
27import android.telephony.SignalStrength;
28import android.util.TimeUtils;
29
30import java.io.FileDescriptor;
31import java.io.PrintWriter;
32import java.util.List;
33
34/**
35 * {@hide}
36 */
37public abstract class ServiceStateTracker extends Handler {
38
39    protected CommandsInterface cm;
40
41    protected PhoneBase mPhoneBase;
42
43    public ServiceState ss = new ServiceState();
44    protected ServiceState newSS = new ServiceState();
45
46    protected CellInfo mLastCellInfo = null;
47
48    // This is final as subclasses alias to a more specific type
49    // so we don't want the reference to change.
50    protected final CellInfo mCellInfo;
51
52    protected SignalStrength mSignalStrength = new SignalStrength();
53
54    // TODO - this should not be public, right now used externally GsmConnetion.
55    public RestrictedState mRestrictedState = new RestrictedState();
56
57    /* The otaspMode passed to PhoneStateListener#onOtaspChanged */
58    static public final int OTASP_UNINITIALIZED = 0;
59    static public final int OTASP_UNKNOWN = 1;
60    static public final int OTASP_NEEDED = 2;
61    static public final int OTASP_NOT_NEEDED = 3;
62
63    /**
64     * A unique identifier to track requests associated with a poll
65     * and ignore stale responses.  The value is a count-down of
66     * expected responses in this pollingContext.
67     */
68    protected int[] pollingContext;
69    protected boolean mDesiredPowerState;
70
71    /**
72     *  Values correspond to ServiceState.RIL_RADIO_TECHNOLOGY_ definitions.
73     */
74    protected int mRilRadioTechnology = 0;
75    protected int mNewRilRadioTechnology = 0;
76
77    /**
78     * By default, strength polling is enabled.  However, if we're
79     * getting unsolicited signal strength updates from the radio, set
80     * value to true and don't bother polling any more.
81     */
82    protected boolean dontPollSignalStrength = false;
83
84    protected RegistrantList mRoamingOnRegistrants = new RegistrantList();
85    protected RegistrantList mRoamingOffRegistrants = new RegistrantList();
86    protected RegistrantList mAttachedRegistrants = new RegistrantList();
87    protected RegistrantList mDetachedRegistrants = new RegistrantList();
88    protected RegistrantList mNetworkAttachedRegistrants = new RegistrantList();
89    protected RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList();
90    protected RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList();
91
92    /* Radio power off pending flag and tag counter */
93    private boolean mPendingRadioPowerOffAfterDataOff = false;
94    private int mPendingRadioPowerOffAfterDataOffTag = 0;
95
96    protected  static final boolean DBG = true;
97
98    /** Signal strength poll rate. */
99    protected static final int POLL_PERIOD_MILLIS = 20 * 1000;
100
101    /** Waiting period before recheck gprs and voice registration. */
102    public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
103
104    /** GSM events */
105    protected static final int EVENT_RADIO_STATE_CHANGED               = 1;
106    protected static final int EVENT_NETWORK_STATE_CHANGED             = 2;
107    protected static final int EVENT_GET_SIGNAL_STRENGTH               = 3;
108    protected static final int EVENT_POLL_STATE_REGISTRATION           = 4;
109    protected static final int EVENT_POLL_STATE_GPRS                   = 5;
110    protected static final int EVENT_POLL_STATE_OPERATOR               = 6;
111    protected static final int EVENT_POLL_SIGNAL_STRENGTH              = 10;
112    protected static final int EVENT_NITZ_TIME                         = 11;
113    protected static final int EVENT_SIGNAL_STRENGTH_UPDATE            = 12;
114    protected static final int EVENT_RADIO_AVAILABLE                   = 13;
115    protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14;
116    protected static final int EVENT_GET_LOC_DONE                      = 15;
117    protected static final int EVENT_SIM_RECORDS_LOADED                = 16;
118    protected static final int EVENT_SIM_READY                         = 17;
119    protected static final int EVENT_LOCATION_UPDATES_ENABLED          = 18;
120    protected static final int EVENT_GET_PREFERRED_NETWORK_TYPE        = 19;
121    protected static final int EVENT_SET_PREFERRED_NETWORK_TYPE        = 20;
122    protected static final int EVENT_RESET_PREFERRED_NETWORK_TYPE      = 21;
123    protected static final int EVENT_CHECK_REPORT_GPRS                 = 22;
124    protected static final int EVENT_RESTRICTED_STATE_CHANGED          = 23;
125
126    /** CDMA events */
127    protected static final int EVENT_POLL_STATE_REGISTRATION_CDMA      = 24;
128    protected static final int EVENT_POLL_STATE_OPERATOR_CDMA          = 25;
129    protected static final int EVENT_RUIM_READY                        = 26;
130    protected static final int EVENT_RUIM_RECORDS_LOADED               = 27;
131    protected static final int EVENT_POLL_SIGNAL_STRENGTH_CDMA         = 28;
132    protected static final int EVENT_GET_SIGNAL_STRENGTH_CDMA          = 29;
133    protected static final int EVENT_NETWORK_STATE_CHANGED_CDMA        = 30;
134    protected static final int EVENT_GET_LOC_DONE_CDMA                 = 31;
135    //protected static final int EVENT_UNUSED                            = 32;
136    protected static final int EVENT_NV_LOADED                         = 33;
137    protected static final int EVENT_POLL_STATE_CDMA_SUBSCRIPTION      = 34;
138    protected static final int EVENT_NV_READY                          = 35;
139    protected static final int EVENT_ERI_FILE_LOADED                   = 36;
140    protected static final int EVENT_OTA_PROVISION_STATUS_CHANGE       = 37;
141    protected static final int EVENT_SET_RADIO_POWER_OFF               = 38;
142    protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED  = 39;
143    protected static final int EVENT_CDMA_PRL_VERSION_CHANGED          = 40;
144    protected static final int EVENT_RADIO_ON                          = 41;
145
146
147    protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
148
149    /**
150     * List of ISO codes for countries that can have an offset of
151     * GMT+0 when not in daylight savings time.  This ignores some
152     * small places such as the Canary Islands (Spain) and
153     * Danmarkshavn (Denmark).  The list must be sorted by code.
154    */
155    protected static final String[] GMT_COUNTRY_CODES = {
156        "bf", // Burkina Faso
157        "ci", // Cote d'Ivoire
158        "eh", // Western Sahara
159        "fo", // Faroe Islands, Denmark
160        "gb", // United Kingdom of Great Britain and Northern Ireland
161        "gh", // Ghana
162        "gm", // Gambia
163        "gn", // Guinea
164        "gw", // Guinea Bissau
165        "ie", // Ireland
166        "lr", // Liberia
167        "is", // Iceland
168        "ma", // Morocco
169        "ml", // Mali
170        "mr", // Mauritania
171        "pt", // Portugal
172        "sl", // Sierra Leone
173        "sn", // Senegal
174        "st", // Sao Tome and Principe
175        "tg", // Togo
176    };
177
178    /** Reason for registration denial. */
179    protected static final String REGISTRATION_DENIED_GEN  = "General";
180    protected static final String REGISTRATION_DENIED_AUTH = "Authentication Failure";
181
182    protected ServiceStateTracker(PhoneBase phoneBase, CellInfo cellInfo) {
183        mPhoneBase = phoneBase;
184        mCellInfo = cellInfo;
185        cm = mPhoneBase.mCM;
186        cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
187    }
188
189    public void dispose() {
190        cm.unSetOnSignalStrengthUpdate(this);
191    }
192
193    public boolean getDesiredPowerState() {
194        return mDesiredPowerState;
195    }
196
197    private SignalStrength mLastSignalStrength = null;
198    protected boolean notifySignalStrength() {
199        boolean notified = false;
200        synchronized(mCellInfo) {
201            if (!mSignalStrength.equals(mLastSignalStrength)) {
202                try {
203                    mPhoneBase.notifySignalStrength();
204                    notified = true;
205                } catch (NullPointerException ex) {
206                    loge("updateSignalStrength() Phone already destroyed: " + ex
207                            + "SignalStrength not notified");
208                }
209            }
210        }
211        return notified;
212    }
213
214    /**
215     * Set the mCellInfo.signalStrength to its default values
216     */
217    protected void setSignalStrengthDefaultValues() {
218        setSignalStrengthDefaultValues(mSignalStrength);
219    }
220
221    /**
222     * Set the signal strength default values
223     */
224    protected void setSignalStrengthDefaultValues(SignalStrength signalStrength) {
225        signalStrength.initialize(99, -1, -1, -1, -1, -1, -1,
226                -1, -1, -1, SignalStrength.INVALID_SNR, -1, isGsmSignalStrength());
227    }
228
229    /**
230     * Return true if this SST is a GSM category device.
231     */
232    protected abstract boolean isGsmSignalStrength();
233
234    /**
235     * Registration point for combined roaming on
236     * combined roaming is true when roaming is true and ONS differs SPN
237     *
238     * @param h handler to notify
239     * @param what what code of message when delivered
240     * @param obj placed in Message.obj
241     */
242    public  void registerForRoamingOn(Handler h, int what, Object obj) {
243        Registrant r = new Registrant(h, what, obj);
244        mRoamingOnRegistrants.add(r);
245
246        if (ss.getRoaming()) {
247            r.notifyRegistrant();
248        }
249    }
250
251    public  void unregisterForRoamingOn(Handler h) {
252        mRoamingOnRegistrants.remove(h);
253    }
254
255    /**
256     * Registration point for combined roaming off
257     * combined roaming is true when roaming is true and ONS differs SPN
258     *
259     * @param h handler to notify
260     * @param what what code of message when delivered
261     * @param obj placed in Message.obj
262     */
263    public  void registerForRoamingOff(Handler h, int what, Object obj) {
264        Registrant r = new Registrant(h, what, obj);
265        mRoamingOffRegistrants.add(r);
266
267        if (!ss.getRoaming()) {
268            r.notifyRegistrant();
269        }
270    }
271
272    public  void unregisterForRoamingOff(Handler h) {
273        mRoamingOffRegistrants.remove(h);
274    }
275
276    /**
277     * Re-register network by toggling preferred network type.
278     * This is a work-around to deregister and register network since there is
279     * no ril api to set COPS=2 (deregister) only.
280     *
281     * @param onComplete is dispatched when this is complete.  it will be
282     * an AsyncResult, and onComplete.obj.exception will be non-null
283     * on failure.
284     */
285    public void reRegisterNetwork(Message onComplete) {
286        cm.getPreferredNetworkType(
287                obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete));
288    }
289
290    public void
291    setRadioPower(boolean power) {
292        mDesiredPowerState = power;
293
294        setPowerStateToDesired();
295    }
296
297    /**
298     * These two flags manage the behavior of the cell lock -- the
299     * lock should be held if either flag is true.  The intention is
300     * to allow temporary acquisition of the lock to get a single
301     * update.  Such a lock grab and release can thus be made to not
302     * interfere with more permanent lock holds -- in other words, the
303     * lock will only be released if both flags are false, and so
304     * releases by temporary users will only affect the lock state if
305     * there is no continuous user.
306     */
307    private boolean mWantContinuousLocationUpdates;
308    private boolean mWantSingleLocationUpdate;
309
310    public void enableSingleLocationUpdate() {
311        if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
312        mWantSingleLocationUpdate = true;
313        cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
314    }
315
316    public void enableLocationUpdates() {
317        if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
318        mWantContinuousLocationUpdates = true;
319        cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
320    }
321
322    protected void disableSingleLocationUpdate() {
323        mWantSingleLocationUpdate = false;
324        if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
325            cm.setLocationUpdates(false, null);
326        }
327    }
328
329    public void disableLocationUpdates() {
330        mWantContinuousLocationUpdates = false;
331        if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
332            cm.setLocationUpdates(false, null);
333        }
334    }
335
336    @Override
337    public void handleMessage(Message msg) {
338        switch (msg.what) {
339            case EVENT_SET_RADIO_POWER_OFF:
340                synchronized(this) {
341                    if (mPendingRadioPowerOffAfterDataOff &&
342                            (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) {
343                        if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
344                        hangupAndPowerOff();
345                        mPendingRadioPowerOffAfterDataOffTag += 1;
346                        mPendingRadioPowerOffAfterDataOff = false;
347                    } else {
348                        log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 +
349                                "!= tag=" + mPendingRadioPowerOffAfterDataOffTag);
350                    }
351                }
352                break;
353
354            default:
355                log("Unhandled message with number: " + msg.what);
356                break;
357        }
358    }
359
360    protected abstract Phone getPhone();
361    protected abstract void handlePollStateResult(int what, AsyncResult ar);
362    protected abstract void updateSpnDisplay();
363    protected abstract void setPowerStateToDesired();
364    protected abstract void log(String s);
365    protected abstract void loge(String s);
366
367    public abstract int getCurrentDataConnectionState();
368    public abstract boolean isConcurrentVoiceAndDataAllowed();
369
370    /**
371     * Registration point for transition into DataConnection attached.
372     * @param h handler to notify
373     * @param what what code of message when delivered
374     * @param obj placed in Message.obj
375     */
376    public void registerForDataConnectionAttached(Handler h, int what, Object obj) {
377        Registrant r = new Registrant(h, what, obj);
378        mAttachedRegistrants.add(r);
379
380        if (getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
381            r.notifyRegistrant();
382        }
383    }
384    public void unregisterForDataConnectionAttached(Handler h) {
385        mAttachedRegistrants.remove(h);
386    }
387
388    /**
389     * Registration point for transition into DataConnection detached.
390     * @param h handler to notify
391     * @param what what code of message when delivered
392     * @param obj placed in Message.obj
393     */
394    public void registerForDataConnectionDetached(Handler h, int what, Object obj) {
395        Registrant r = new Registrant(h, what, obj);
396        mDetachedRegistrants.add(r);
397
398        if (getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
399            r.notifyRegistrant();
400        }
401    }
402    public void unregisterForDataConnectionDetached(Handler h) {
403        mDetachedRegistrants.remove(h);
404    }
405
406    /**
407     * Registration point for transition into network attached.
408     * @param h handler to notify
409     * @param what what code of message when delivered
410     * @param obj in Message.obj
411     */
412    public void registerForNetworkAttached(Handler h, int what, Object obj) {
413        Registrant r = new Registrant(h, what, obj);
414
415        mNetworkAttachedRegistrants.add(r);
416        if (ss.getState() == ServiceState.STATE_IN_SERVICE) {
417            r.notifyRegistrant();
418        }
419    }
420    public void unregisterForNetworkAttached(Handler h) {
421        mNetworkAttachedRegistrants.remove(h);
422    }
423
424    /**
425     * Registration point for transition into packet service restricted zone.
426     * @param h handler to notify
427     * @param what what code of message when delivered
428     * @param obj placed in Message.obj
429     */
430    public void registerForPsRestrictedEnabled(Handler h, int what, Object obj) {
431        Registrant r = new Registrant(h, what, obj);
432        mPsRestrictEnabledRegistrants.add(r);
433
434        if (mRestrictedState.isPsRestricted()) {
435            r.notifyRegistrant();
436        }
437    }
438
439    public void unregisterForPsRestrictedEnabled(Handler h) {
440        mPsRestrictEnabledRegistrants.remove(h);
441    }
442
443    /**
444     * Registration point for transition out of packet service restricted zone.
445     * @param h handler to notify
446     * @param what what code of message when delivered
447     * @param obj placed in Message.obj
448     */
449    public void registerForPsRestrictedDisabled(Handler h, int what, Object obj) {
450        Registrant r = new Registrant(h, what, obj);
451        mPsRestrictDisabledRegistrants.add(r);
452
453        if (mRestrictedState.isPsRestricted()) {
454            r.notifyRegistrant();
455        }
456    }
457
458    public void unregisterForPsRestrictedDisabled(Handler h) {
459        mPsRestrictDisabledRegistrants.remove(h);
460    }
461
462    /**
463     * Clean up existing voice and data connection then turn off radio power.
464     *
465     * Hang up the existing voice calls to decrease call drop rate.
466     */
467    public void powerOffRadioSafely(DataConnectionTracker dcTracker) {
468        synchronized (this) {
469            if (!mPendingRadioPowerOffAfterDataOff) {
470                // To minimize race conditions we call cleanUpAllConnections on
471                // both if else paths instead of before this isDisconnected test.
472                if (dcTracker.isDisconnected()) {
473                    // To minimize race conditions we do this after isDisconnected
474                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
475                    if (DBG) log("Data disconnected, turn off radio right away.");
476                    hangupAndPowerOff();
477                } else {
478                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
479                    Message msg = Message.obtain(this);
480                    msg.what = EVENT_SET_RADIO_POWER_OFF;
481                    msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
482                    if (sendMessageDelayed(msg, 30000)) {
483                        if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
484                        mPendingRadioPowerOffAfterDataOff = true;
485                    } else {
486                        log("Cannot send delayed Msg, turn off radio right away.");
487                        hangupAndPowerOff();
488                    }
489                }
490            }
491        }
492    }
493
494    /**
495     * process the pending request to turn radio off after data is disconnected
496     *
497     * return true if there is pending request to process; false otherwise.
498     */
499    public boolean processPendingRadioPowerOffAfterDataOff() {
500        synchronized(this) {
501            if (mPendingRadioPowerOffAfterDataOff) {
502                if (DBG) log("Process pending request to turn radio off.");
503                mPendingRadioPowerOffAfterDataOffTag += 1;
504                hangupAndPowerOff();
505                mPendingRadioPowerOffAfterDataOff = false;
506                return true;
507            }
508            return false;
509        }
510    }
511
512    /**
513     * Hang up all voice call and turn off radio. Implemented by derived class.
514     */
515    protected abstract void hangupAndPowerOff();
516
517    /** Cancel a pending (if any) pollState() operation */
518    protected void cancelPollState() {
519        // This will effectively cancel the rest of the poll requests.
520        pollingContext = new int[1];
521    }
522
523    /**
524     * Return true if time zone needs fixing.
525     *
526     * @param phoneBase
527     * @param operatorNumeric
528     * @param prevOperatorNumeric
529     * @param needToFixTimeZone
530     * @return true if time zone needs to be fixed
531     */
532    protected boolean shouldFixTimeZoneNow(PhoneBase phoneBase, String operatorNumeric,
533            String prevOperatorNumeric, boolean needToFixTimeZone) {
534        // Return false if the mcc isn't valid as we don't know where we are.
535        // Return true if we have an IccCard and the mcc changed or we
536        // need to fix it because when the NITZ time came in we didn't
537        // know the country code.
538
539        // If mcc is invalid then we'll return false
540        int mcc;
541        try {
542            mcc = Integer.parseInt(operatorNumeric.substring(0, 3));
543        } catch (Exception e) {
544            if (DBG) {
545                log("shouldFixTimeZoneNow: no mcc, operatorNumeric=" + operatorNumeric +
546                        " retVal=false");
547            }
548            return false;
549        }
550
551        // If prevMcc is invalid will make it different from mcc
552        // so we'll return true if the card exists.
553        int prevMcc;
554        try {
555            prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3));
556        } catch (Exception e) {
557            prevMcc = mcc + 1;
558        }
559
560        // Determine if the Icc card exists
561        IccCard iccCard = phoneBase.getIccCard();
562        boolean iccCardExist = (iccCard != null) && iccCard.getState().iccCardExist();
563
564        // Determine retVal
565        boolean retVal = ((iccCardExist && (mcc != prevMcc)) || needToFixTimeZone);
566        if (DBG) {
567            long ctm = System.currentTimeMillis();
568            log("shouldFixTimeZoneNow: retVal=" + retVal +
569                    " iccCard=" + iccCard +
570                    " iccCard.state=" + (iccCard == null ? "null" : iccCard.getState().toString()) +
571                    " iccCardExist=" + iccCardExist +
572                    " operatorNumeric=" + operatorNumeric + " mcc=" + mcc +
573                    " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc +
574                    " needToFixTimeZone=" + needToFixTimeZone +
575                    " ltod=" + TimeUtils.logTimeOfDay(ctm));
576        }
577        return retVal;
578    }
579
580    /**
581     * @return all available cell information or null if none.
582     */
583    public List<CellInfo> getAllCellInfo() {
584        return null;
585    }
586
587    /**
588     * @return signal strength
589     */
590    public SignalStrength getSignalStrength() {
591        synchronized(mCellInfo) {
592            return mSignalStrength;
593        }
594    }
595
596    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
597        pw.println("ServiceStateTracker:");
598        pw.println(" ss=" + ss);
599        pw.println(" newSS=" + newSS);
600        pw.println(" mCellInfo=" + mCellInfo);
601        pw.println(" mRestrictedState=" + mRestrictedState);
602        pw.println(" pollingContext=" + pollingContext);
603        pw.println(" mDesiredPowerState=" + mDesiredPowerState);
604        pw.println(" mRilRadioTechnology=" + mRilRadioTechnology);
605        pw.println(" mNewRilRadioTechnology=" + mNewRilRadioTechnology);
606        pw.println(" dontPollSignalStrength=" + dontPollSignalStrength);
607        pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff);
608        pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag);
609    }
610}
611