| /* |
| * 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; |
| import android.util.TimeUtils; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| |
| /** |
| * {@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.RIL_RADIO_TECHNOLOGY_ definitions. |
| */ |
| protected int mRilRadioTechnology = 0; |
| protected int mNewRilRadioTechnology = 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 int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 39; |
| protected static final int EVENT_CDMA_PRL_VERSION_CHANGED = 40; |
| protected static final int EVENT_RADIO_ON = 41; |
| |
| |
| 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 |
| "gb", // United Kingdom of Great Britain and Northern Ireland |
| "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 |
| }; |
| |
| /** 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]; |
| } |
| |
| /** |
| * Return true if time zone needs fixing. |
| * |
| * @param phoneBase |
| * @param operatorNumeric |
| * @param prevOperatorNumeric |
| * @param needToFixTimeZone |
| * @return true if time zone needs to be fixed |
| */ |
| protected boolean shouldFixTimeZoneNow(PhoneBase phoneBase, String operatorNumeric, |
| String prevOperatorNumeric, boolean needToFixTimeZone) { |
| // Return false if the mcc isn't valid as we don't know where we are. |
| // Return true if we have an IccCard and the mcc changed or we |
| // need to fix it because when the NITZ time came in we didn't |
| // know the country code. |
| |
| // If mcc is invalid then we'll return false |
| int mcc; |
| try { |
| mcc = Integer.parseInt(operatorNumeric.substring(0, 3)); |
| } catch (Exception e) { |
| if (DBG) { |
| log("shouldFixTimeZoneNow: no mcc, operatorNumeric=" + operatorNumeric + |
| " retVal=false"); |
| } |
| return false; |
| } |
| |
| // If prevMcc is invalid will make it different from mcc |
| // so we'll return true if the card exists. |
| int prevMcc; |
| try { |
| prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3)); |
| } catch (Exception e) { |
| prevMcc = mcc + 1; |
| } |
| |
| // Determine if the Icc card exists |
| IccCard iccCard = phoneBase.getIccCard(); |
| boolean iccCardExist = (iccCard != null) && iccCard.getState().iccCardExist(); |
| |
| // Determine retVal |
| boolean retVal = ((iccCardExist && (mcc != prevMcc)) || needToFixTimeZone); |
| if (DBG) { |
| long ctm = System.currentTimeMillis(); |
| log("shouldFixTimeZoneNow: retVal=" + retVal + |
| " iccCard=" + iccCard + |
| " iccCard.state=" + (iccCard == null ? "null" : iccCard.getState().toString()) + |
| " iccCardExist=" + iccCardExist + |
| " operatorNumeric=" + operatorNumeric + " mcc=" + mcc + |
| " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc + |
| " needToFixTimeZone=" + needToFixTimeZone + |
| " ltod=" + TimeUtils.logTimeOfDay(ctm)); |
| } |
| return retVal; |
| } |
| |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| pw.println("ServiceStateTracker:"); |
| pw.println(" ss=" + ss); |
| pw.println(" newSS=" + newSS); |
| pw.println(" mSignalStrength=" + mSignalStrength); |
| pw.println(" mRestrictedState=" + mRestrictedState); |
| pw.println(" pollingContext=" + pollingContext); |
| pw.println(" mDesiredPowerState=" + mDesiredPowerState); |
| pw.println(" mRilRadioTechnology=" + mRilRadioTechnology); |
| pw.println(" mNewRilRadioTechnology=" + mNewRilRadioTechnology); |
| pw.println(" dontPollSignalStrength=" + dontPollSignalStrength); |
| pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff); |
| pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag); |
| } |
| } |