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