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