450 lines
17 KiB
Java
450 lines
17 KiB
Java
/*
|
|
* 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;
|
|
|
|
/**
|
|
* 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) {
|
|
if (dcTracker.isAnyActiveDataConnections()) {
|
|
dcTracker.cleanUpAllConnections(null);
|
|
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();
|
|
}
|
|
} else {
|
|
dcTracker.cleanUpAllConnections(null);
|
|
if (DBG) log("Data disconnected, 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];
|
|
}
|
|
}
|