| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.telephony; |
| |
| import android.os.AsyncResult; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.Registrant; |
| import android.os.RegistrantList; |
| import android.telephony.ServiceState; |
| import android.telephony.SignalStrength; |
| |
| /** |
| * {@hide} |
| */ |
| public abstract class ServiceStateTracker extends Handler { |
| |
| protected CommandsInterface cm; |
| |
| public ServiceState ss; |
| protected ServiceState newSS; |
| |
| public SignalStrength mSignalStrength; |
| |
| // TODO - this should not be public |
| public RestrictedState mRestrictedState = new RestrictedState(); |
| |
| /* The otaspMode passed to PhoneStateListener#onOtaspChanged */ |
| static public final int OTASP_UNINITIALIZED = 0; |
| static public final int OTASP_UNKNOWN = 1; |
| static public final int OTASP_NEEDED = 2; |
| static public final int OTASP_NOT_NEEDED = 3; |
| |
| /** |
| * A unique identifier to track requests associated with a poll |
| * and ignore stale responses. The value is a count-down of |
| * expected responses in this pollingContext. |
| */ |
| protected int[] pollingContext; |
| protected boolean mDesiredPowerState; |
| |
| /** |
| * Values correspond to ServiceState.RADIO_TECHNOLOGY_ definitions. |
| */ |
| protected int mRadioTechnology = 0; |
| protected int mNewRadioTechnology = 0; |
| |
| /** |
| * By default, strength polling is enabled. However, if we're |
| * getting unsolicited signal strength updates from the radio, set |
| * value to true and don't bother polling any more. |
| */ |
| protected boolean dontPollSignalStrength = false; |
| |
| protected RegistrantList mRoamingOnRegistrants = new RegistrantList(); |
| protected RegistrantList mRoamingOffRegistrants = new RegistrantList(); |
| protected RegistrantList mAttachedRegistrants = new RegistrantList(); |
| protected RegistrantList mDetachedRegistrants = new RegistrantList(); |
| protected RegistrantList mNetworkAttachedRegistrants = new RegistrantList(); |
| protected RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList(); |
| protected RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList(); |
| |
| /* Radio power off pending flag and tag counter */ |
| private boolean mPendingRadioPowerOffAfterDataOff = false; |
| private int mPendingRadioPowerOffAfterDataOffTag = 0; |
| |
| protected static final boolean DBG = true; |
| |
| /** Signal strength poll rate. */ |
| protected static final int POLL_PERIOD_MILLIS = 20 * 1000; |
| |
| /** Waiting period before recheck gprs and voice registration. */ |
| public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000; |
| |
| /** GSM events */ |
| protected static final int EVENT_RADIO_STATE_CHANGED = 1; |
| protected static final int EVENT_NETWORK_STATE_CHANGED = 2; |
| protected static final int EVENT_GET_SIGNAL_STRENGTH = 3; |
| protected static final int EVENT_POLL_STATE_REGISTRATION = 4; |
| protected static final int EVENT_POLL_STATE_GPRS = 5; |
| protected static final int EVENT_POLL_STATE_OPERATOR = 6; |
| protected static final int EVENT_POLL_SIGNAL_STRENGTH = 10; |
| protected static final int EVENT_NITZ_TIME = 11; |
| protected static final int EVENT_SIGNAL_STRENGTH_UPDATE = 12; |
| protected static final int EVENT_RADIO_AVAILABLE = 13; |
| protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14; |
| protected static final int EVENT_GET_LOC_DONE = 15; |
| protected static final int EVENT_SIM_RECORDS_LOADED = 16; |
| protected static final int EVENT_SIM_READY = 17; |
| protected static final int EVENT_LOCATION_UPDATES_ENABLED = 18; |
| protected static final int EVENT_GET_PREFERRED_NETWORK_TYPE = 19; |
| protected static final int EVENT_SET_PREFERRED_NETWORK_TYPE = 20; |
| protected static final int EVENT_RESET_PREFERRED_NETWORK_TYPE = 21; |
| protected static final int EVENT_CHECK_REPORT_GPRS = 22; |
| protected static final int EVENT_RESTRICTED_STATE_CHANGED = 23; |
| |
| /** CDMA events */ |
| protected static final int EVENT_POLL_STATE_REGISTRATION_CDMA = 24; |
| protected static final int EVENT_POLL_STATE_OPERATOR_CDMA = 25; |
| protected static final int EVENT_RUIM_READY = 26; |
| protected static final int EVENT_RUIM_RECORDS_LOADED = 27; |
| protected static final int EVENT_POLL_SIGNAL_STRENGTH_CDMA = 28; |
| protected static final int EVENT_GET_SIGNAL_STRENGTH_CDMA = 29; |
| protected static final int EVENT_NETWORK_STATE_CHANGED_CDMA = 30; |
| protected static final int EVENT_GET_LOC_DONE_CDMA = 31; |
| protected static final int EVENT_SIGNAL_STRENGTH_UPDATE_CDMA = 32; |
| protected static final int EVENT_NV_LOADED = 33; |
| protected static final int EVENT_POLL_STATE_CDMA_SUBSCRIPTION = 34; |
| protected static final int EVENT_NV_READY = 35; |
| protected static final int EVENT_ERI_FILE_LOADED = 36; |
| protected static final int EVENT_OTA_PROVISION_STATUS_CHANGE = 37; |
| protected static final int EVENT_SET_RADIO_POWER_OFF = 38; |
| |
| protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; |
| |
| /** |
| * List of ISO codes for countries that can have an offset of |
| * GMT+0 when not in daylight savings time. This ignores some |
| * small places such as the Canary Islands (Spain) and |
| * Danmarkshavn (Denmark). The list must be sorted by code. |
| */ |
| protected static final String[] GMT_COUNTRY_CODES = { |
| "bf", // Burkina Faso |
| "ci", // Cote d'Ivoire |
| "eh", // Western Sahara |
| "fo", // Faroe Islands, Denmark |
| "gh", // Ghana |
| "gm", // Gambia |
| "gn", // Guinea |
| "gw", // Guinea Bissau |
| "ie", // Ireland |
| "lr", // Liberia |
| "is", // Iceland |
| "ma", // Morocco |
| "ml", // Mali |
| "mr", // Mauritania |
| "pt", // Portugal |
| "sl", // Sierra Leone |
| "sn", // Senegal |
| "st", // Sao Tome and Principe |
| "tg", // Togo |
| "uk", // U.K |
| }; |
| |
| /** Reason for registration denial. */ |
| protected static final String REGISTRATION_DENIED_GEN = "General"; |
| protected static final String REGISTRATION_DENIED_AUTH = "Authentication Failure"; |
| |
| public ServiceStateTracker() { |
| } |
| |
| public boolean getDesiredPowerState() { |
| return mDesiredPowerState; |
| } |
| |
| /** |
| * Registration point for combined roaming on |
| * combined roaming is true when roaming is true and ONS differs SPN |
| * |
| * @param h handler to notify |
| * @param what what code of message when delivered |
| * @param obj placed in Message.obj |
| */ |
| public void registerForRoamingOn(Handler h, int what, Object obj) { |
| Registrant r = new Registrant(h, what, obj); |
| mRoamingOnRegistrants.add(r); |
| |
| if (ss.getRoaming()) { |
| r.notifyRegistrant(); |
| } |
| } |
| |
| public void unregisterForRoamingOn(Handler h) { |
| mRoamingOnRegistrants.remove(h); |
| } |
| |
| /** |
| * Registration point for combined roaming off |
| * combined roaming is true when roaming is true and ONS differs SPN |
| * |
| * @param h handler to notify |
| * @param what what code of message when delivered |
| * @param obj placed in Message.obj |
| */ |
| public void registerForRoamingOff(Handler h, int what, Object obj) { |
| Registrant r = new Registrant(h, what, obj); |
| mRoamingOffRegistrants.add(r); |
| |
| if (!ss.getRoaming()) { |
| r.notifyRegistrant(); |
| } |
| } |
| |
| public void unregisterForRoamingOff(Handler h) { |
| mRoamingOffRegistrants.remove(h); |
| } |
| |
| /** |
| * Re-register network by toggling preferred network type. |
| * This is a work-around to deregister and register network since there is |
| * no ril api to set COPS=2 (deregister) only. |
| * |
| * @param onComplete is dispatched when this is complete. it will be |
| * an AsyncResult, and onComplete.obj.exception will be non-null |
| * on failure. |
| */ |
| public void reRegisterNetwork(Message onComplete) { |
| cm.getPreferredNetworkType( |
| obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete)); |
| } |
| |
| public void |
| setRadioPower(boolean power) { |
| mDesiredPowerState = power; |
| |
| setPowerStateToDesired(); |
| } |
| |
| /** |
| * These two flags manage the behavior of the cell lock -- the |
| * lock should be held if either flag is true. The intention is |
| * to allow temporary acquisition of the lock to get a single |
| * update. Such a lock grab and release can thus be made to not |
| * interfere with more permanent lock holds -- in other words, the |
| * lock will only be released if both flags are false, and so |
| * releases by temporary users will only affect the lock state if |
| * there is no continuous user. |
| */ |
| private boolean mWantContinuousLocationUpdates; |
| private boolean mWantSingleLocationUpdate; |
| |
| public void enableSingleLocationUpdate() { |
| if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return; |
| mWantSingleLocationUpdate = true; |
| cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED)); |
| } |
| |
| public void enableLocationUpdates() { |
| if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return; |
| mWantContinuousLocationUpdates = true; |
| cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED)); |
| } |
| |
| protected void disableSingleLocationUpdate() { |
| mWantSingleLocationUpdate = false; |
| if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) { |
| cm.setLocationUpdates(false, null); |
| } |
| } |
| |
| public void disableLocationUpdates() { |
| mWantContinuousLocationUpdates = false; |
| if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) { |
| cm.setLocationUpdates(false, null); |
| } |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| case EVENT_SET_RADIO_POWER_OFF: |
| synchronized(this) { |
| if (mPendingRadioPowerOffAfterDataOff && |
| (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) { |
| if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now."); |
| hangupAndPowerOff(); |
| mPendingRadioPowerOffAfterDataOffTag += 1; |
| mPendingRadioPowerOffAfterDataOff = false; |
| } else { |
| log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 + |
| "!= tag=" + mPendingRadioPowerOffAfterDataOffTag); |
| } |
| } |
| break; |
| |
| default: |
| log("Unhandled message with number: " + msg.what); |
| break; |
| } |
| } |
| |
| protected abstract Phone getPhone(); |
| protected abstract void handlePollStateResult(int what, AsyncResult ar); |
| protected abstract void updateSpnDisplay(); |
| protected abstract void setPowerStateToDesired(); |
| protected abstract void log(String s); |
| protected abstract void loge(String s); |
| |
| public abstract int getCurrentDataConnectionState(); |
| public abstract boolean isConcurrentVoiceAndDataAllowed(); |
| |
| /** |
| * Registration point for transition into DataConnection attached. |
| * @param h handler to notify |
| * @param what what code of message when delivered |
| * @param obj placed in Message.obj |
| */ |
| public void registerForDataConnectionAttached(Handler h, int what, Object obj) { |
| Registrant r = new Registrant(h, what, obj); |
| mAttachedRegistrants.add(r); |
| |
| if (getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) { |
| r.notifyRegistrant(); |
| } |
| } |
| public void unregisterForDataConnectionAttached(Handler h) { |
| mAttachedRegistrants.remove(h); |
| } |
| |
| /** |
| * Registration point for transition into DataConnection detached. |
| * @param h handler to notify |
| * @param what what code of message when delivered |
| * @param obj placed in Message.obj |
| */ |
| public void registerForDataConnectionDetached(Handler h, int what, Object obj) { |
| Registrant r = new Registrant(h, what, obj); |
| mDetachedRegistrants.add(r); |
| |
| if (getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) { |
| r.notifyRegistrant(); |
| } |
| } |
| public void unregisterForDataConnectionDetached(Handler h) { |
| mDetachedRegistrants.remove(h); |
| } |
| |
| /** |
| * Registration point for transition into network attached. |
| * @param h handler to notify |
| * @param what what code of message when delivered |
| * @param obj in Message.obj |
| */ |
| public void registerForNetworkAttached(Handler h, int what, Object obj) { |
| Registrant r = new Registrant(h, what, obj); |
| |
| mNetworkAttachedRegistrants.add(r); |
| if (ss.getState() == ServiceState.STATE_IN_SERVICE) { |
| r.notifyRegistrant(); |
| } |
| } |
| public void unregisterForNetworkAttached(Handler h) { |
| mNetworkAttachedRegistrants.remove(h); |
| } |
| |
| /** |
| * Registration point for transition into packet service restricted zone. |
| * @param h handler to notify |
| * @param what what code of message when delivered |
| * @param obj placed in Message.obj |
| */ |
| public void registerForPsRestrictedEnabled(Handler h, int what, Object obj) { |
| Registrant r = new Registrant(h, what, obj); |
| mPsRestrictEnabledRegistrants.add(r); |
| |
| if (mRestrictedState.isPsRestricted()) { |
| r.notifyRegistrant(); |
| } |
| } |
| |
| public void unregisterForPsRestrictedEnabled(Handler h) { |
| mPsRestrictEnabledRegistrants.remove(h); |
| } |
| |
| /** |
| * Registration point for transition out of packet service restricted zone. |
| * @param h handler to notify |
| * @param what what code of message when delivered |
| * @param obj placed in Message.obj |
| */ |
| public void registerForPsRestrictedDisabled(Handler h, int what, Object obj) { |
| Registrant r = new Registrant(h, what, obj); |
| mPsRestrictDisabledRegistrants.add(r); |
| |
| if (mRestrictedState.isPsRestricted()) { |
| r.notifyRegistrant(); |
| } |
| } |
| |
| public void unregisterForPsRestrictedDisabled(Handler h) { |
| mPsRestrictDisabledRegistrants.remove(h); |
| } |
| |
| /** |
| * Clean up existing voice and data connection then turn off radio power. |
| * |
| * Hang up the existing voice calls to decrease call drop rate. |
| */ |
| public void powerOffRadioSafely(DataConnectionTracker dcTracker) { |
| synchronized (this) { |
| if (!mPendingRadioPowerOffAfterDataOff) { |
| // To minimize race conditions we call cleanUpAllConnections on |
| // both if else paths instead of before this isDisconnected test. |
| if (dcTracker.isDisconnected()) { |
| // To minimize race conditions we do this after isDisconnected |
| dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF); |
| if (DBG) log("Data disconnected, turn off radio right away."); |
| hangupAndPowerOff(); |
| } else { |
| dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF); |
| Message msg = Message.obtain(this); |
| msg.what = EVENT_SET_RADIO_POWER_OFF; |
| msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag; |
| if (sendMessageDelayed(msg, 30000)) { |
| if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio."); |
| mPendingRadioPowerOffAfterDataOff = true; |
| } else { |
| log("Cannot send delayed Msg, turn off radio right away."); |
| hangupAndPowerOff(); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * process the pending request to turn radio off after data is disconnected |
| * |
| * return true if there is pending request to process; false otherwise. |
| */ |
| public boolean processPendingRadioPowerOffAfterDataOff() { |
| synchronized(this) { |
| if (mPendingRadioPowerOffAfterDataOff) { |
| if (DBG) log("Process pending request to turn radio off."); |
| mPendingRadioPowerOffAfterDataOffTag += 1; |
| hangupAndPowerOff(); |
| mPendingRadioPowerOffAfterDataOff = false; |
| return true; |
| } |
| return false; |
| } |
| } |
| |
| /** |
| * Hang up all voice call and turn off radio. Implemented by derived class. |
| */ |
| protected abstract void hangupAndPowerOff(); |
| |
| /** Cancel a pending (if any) pollState() operation */ |
| protected void cancelPollState() { |
| // This will effectively cancel the rest of the poll requests. |
| pollingContext = new int[1]; |
| } |
| } |