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