/* * 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.app.PendingIntent; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; import android.os.RemoteException; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; import android.util.Log; import java.util.ArrayList; /** * {@hide} * */ public abstract class DataConnectionTracker extends Handler { protected static final boolean DBG = true; protected final String LOG_TAG = "DataConnectionTracker"; /** * IDLE: ready to start data connection setup, default state * INITING: state of issued setupDefaultPDP() but not finish yet * CONNECTING: state of issued startPppd() but not finish yet * SCANNING: data connection fails with one apn but other apns are available * ready to start data connection on other apns (before INITING) * CONNECTED: IP connection is setup * DISCONNECTING: Connection.disconnect() has been called, but PDP * context is not yet deactivated * FAILED: data connection fail for all apns settings * * getDataConnectionState() maps State to DataState * FAILED or IDLE : DISCONNECTED * INITING or CONNECTING or SCANNING: CONNECTING * CONNECTED : CONNECTED or DISCONNECTING */ public enum State { IDLE, INITING, CONNECTING, SCANNING, CONNECTED, DISCONNECTING, FAILED } public enum Activity { NONE, DATAIN, DATAOUT, DATAINANDOUT, DORMANT } /***** Event Codes *****/ protected static final int EVENT_DATA_SETUP_COMPLETE = 1; protected static final int EVENT_RADIO_AVAILABLE = 3; protected static final int EVENT_RECORDS_LOADED = 4; protected static final int EVENT_TRY_SETUP_DATA = 5; protected static final int EVENT_DATA_STATE_CHANGED = 6; protected static final int EVENT_POLL_PDP = 7; protected static final int EVENT_GET_PDP_LIST_COMPLETE = 11; protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 12; protected static final int EVENT_VOICE_CALL_STARTED = 14; protected static final int EVENT_VOICE_CALL_ENDED = 15; protected static final int EVENT_GPRS_DETACHED = 19; protected static final int EVENT_LINK_STATE_CHANGED = 20; protected static final int EVENT_ROAMING_ON = 21; protected static final int EVENT_ROAMING_OFF = 22; protected static final int EVENT_ENABLE_NEW_APN = 23; protected static final int EVENT_RESTORE_DEFAULT_APN = 24; protected static final int EVENT_DISCONNECT_DONE = 25; protected static final int EVENT_GPRS_ATTACHED = 26; protected static final int EVENT_START_NETSTAT_POLL = 27; protected static final int EVENT_START_RECOVERY = 28; protected static final int EVENT_APN_CHANGED = 29; protected static final int EVENT_CDMA_DATA_DETACHED = 30; protected static final int EVENT_NV_READY = 31; protected static final int EVENT_PS_RESTRICT_ENABLED = 32; protected static final int EVENT_PS_RESTRICT_DISABLED = 33; public static final int EVENT_CLEAN_UP_CONNECTION = 34; protected static final int EVENT_CDMA_OTA_PROVISION = 35; protected static final int EVENT_RESTART_RADIO = 36; protected static final int EVENT_SET_MASTER_DATA_ENABLE = 37; protected static final int EVENT_RESET_DONE = 38; /***** Constants *****/ protected static final int APN_INVALID_ID = -1; protected static final int APN_DEFAULT_ID = 0; protected static final int APN_MMS_ID = 1; protected static final int APN_SUPL_ID = 2; protected static final int APN_DUN_ID = 3; protected static final int APN_HIPRI_ID = 4; protected static final int APN_NUM_TYPES = 5; protected static final int DISABLED = 0; protected static final int ENABLED = 1; // responds to the setDataEnabled call - used independently from the APN requests protected boolean mMasterDataEnabled = true; protected boolean[] dataEnabled = new boolean[APN_NUM_TYPES]; protected int enabledCount = 0; /* Currently requested APN type */ protected String mRequestedApnType = Phone.APN_TYPE_DEFAULT; /** Retry configuration: A doubling of retry times from 5secs to 30minutes */ protected static final String DEFAULT_DATA_RETRY_CONFIG = "default_randomization=2000," + "5000,10000,20000,40000,80000:5000,160000:5000," + "320000:5000,640000:5000,1280000:5000,1800000:5000"; /** Retry configuration for secondary networks: 4 tries in 20 sec */ protected static final String SECONDARY_DATA_RETRY_CONFIG = "max_retries=3, 5000, 5000, 5000"; /** Slow poll when attempting connection recovery. */ protected static final int POLL_NETSTAT_SLOW_MILLIS = 5000; /** Default ping deadline, in seconds. */ protected static final int DEFAULT_PING_DEADLINE = 5; /** Default max failure count before attempting to network re-registration. */ protected static final int DEFAULT_MAX_PDP_RESET_FAIL = 3; /** * After detecting a potential connection problem, this is the max number * of subsequent polls before attempting a radio reset. At this point, * poll interval is 5 seconds (POLL_NETSTAT_SLOW_MILLIS), so set this to * poll for about 2 more minutes. */ protected static final int NO_RECV_POLL_LIMIT = 24; // 1 sec. default polling interval when screen is on. protected static final int POLL_NETSTAT_MILLIS = 1000; // 10 min. default polling interval when screen is off. protected static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10; // 2 min for round trip time protected static final int POLL_LONGEST_RTT = 120 * 1000; // 10 for packets without ack protected static final int NUMBER_SENT_PACKETS_OF_HANG = 10; // how long to wait before switching back to default APN protected static final int RESTORE_DEFAULT_APN_DELAY = 1 * 60 * 1000; // system property that can override the above value protected static final String APN_RESTORE_DELAY_PROP_NAME = "android.telephony.apn-restore"; // represents an invalid IP address protected static final String NULL_IP = "0.0.0.0"; // member variables protected PhoneBase phone; protected Activity activity = Activity.NONE; protected State state = State.IDLE; protected Handler mDataConnectionTracker = null; protected long txPkts, rxPkts, sentSinceLastRecv; protected int netStatPollPeriod; protected int mNoRecvPollCount = 0; protected boolean netStatPollEnabled = false; /** Manage the behavior of data retry after failure */ protected RetryManager mRetryMgr = new RetryManager(); // wifi connection status will be updated by sticky intent protected boolean mIsWifiConnected = false; /** Intent sent when the reconnect alarm fires. */ protected PendingIntent mReconnectIntent = null; /** CID of active data connection */ protected int cidActive; /** * Default constructor */ protected DataConnectionTracker(PhoneBase phone) { super(); this.phone = phone; } public abstract void dispose(); public Activity getActivity() { return activity; } public State getState() { return state; } public String getStateInString() { switch (state) { case IDLE: return "IDLE"; case INITING: return "INIT"; case CONNECTING: return "CING"; case SCANNING: return "SCAN"; case CONNECTED: return "CNTD"; case DISCONNECTING: return "DING"; case FAILED: return "FAIL"; default: return "ERRO"; } } /** * The data connection is expected to be setup while device * 1. has Icc card * 2. registered for data service * 3. user doesn't explicitly disable data service * 4. wifi is not on * * @return false while no data connection if all above requirements are met. */ public abstract boolean isDataConnectionAsDesired(); //The data roaming setting is now located in the shared preferences. // See if the requested preference value is the same as that stored in // the shared values. If it is not, then update it. public void setDataOnRoamingEnabled(boolean enabled) { if (getDataOnRoamingEnabled() != enabled) { Settings.Secure.putInt(phone.getContext().getContentResolver(), Settings.Secure.DATA_ROAMING, enabled ? 1 : 0); if (phone.getServiceState().getRoaming()) { if (enabled) { mRetryMgr.resetRetryCount(); } sendMessage(obtainMessage(EVENT_ROAMING_ON)); } } } //Retrieve the data roaming setting from the shared preferences. public boolean getDataOnRoamingEnabled() { try { return Settings.Secure.getInt(phone.getContext().getContentResolver(), Settings.Secure.DATA_ROAMING) > 0; } catch (SettingNotFoundException snfe) { return false; } } // abstract handler methods protected abstract boolean onTrySetupData(String reason); protected abstract void onRoamingOff(); protected abstract void onRoamingOn(); protected abstract void onRadioAvailable(); protected abstract void onRadioOffOrNotAvailable(); protected abstract void onDataSetupComplete(AsyncResult ar); protected abstract void onDisconnectDone(AsyncResult ar); protected abstract void onResetDone(AsyncResult ar); protected abstract void onVoiceCallStarted(); protected abstract void onVoiceCallEnded(); protected abstract void onCleanUpConnection(boolean tearDown, String reason); @Override public void handleMessage (Message msg) { switch (msg.what) { case EVENT_ENABLE_NEW_APN: onEnableApn(msg.arg1, msg.arg2); break; case EVENT_TRY_SETUP_DATA: String reason = null; if (msg.obj instanceof String) { reason = (String)msg.obj; } onTrySetupData(reason); break; case EVENT_ROAMING_OFF: if (getDataOnRoamingEnabled() == false) { mRetryMgr.resetRetryCount(); } onRoamingOff(); break; case EVENT_ROAMING_ON: onRoamingOn(); break; case EVENT_RADIO_AVAILABLE: onRadioAvailable(); break; case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: onRadioOffOrNotAvailable(); break; case EVENT_DATA_SETUP_COMPLETE: cidActive = msg.arg1; onDataSetupComplete((AsyncResult) msg.obj); break; case EVENT_DISCONNECT_DONE: onDisconnectDone((AsyncResult) msg.obj); break; case EVENT_VOICE_CALL_STARTED: onVoiceCallStarted(); break; case EVENT_VOICE_CALL_ENDED: onVoiceCallEnded(); break; case EVENT_CLEAN_UP_CONNECTION: boolean tearDown = (msg.arg1 == 0) ? false : true; onCleanUpConnection(tearDown, (String)msg.obj); break; case EVENT_SET_MASTER_DATA_ENABLE: boolean enabled = (msg.arg1 == ENABLED) ? true : false; onSetDataEnabled(enabled); break; case EVENT_RESET_DONE: onResetDone((AsyncResult) msg.obj); break; default: Log.e("DATA", "Unidentified event = " + msg.what); break; } } /** * Report the current state of data connectivity (enabled or disabled) * @return {@code false} if data connectivity has been explicitly disabled, * {@code true} otherwise. */ public synchronized boolean getDataEnabled() { return dataEnabled[APN_DEFAULT_ID]; } /** * Report on whether data connectivity is enabled * @return {@code false} if data connectivity has been explicitly disabled, * {@code true} otherwise. */ public boolean getAnyDataEnabled() { return (enabledCount != 0); } protected abstract void startNetStatPoll(); protected abstract void stopNetStatPoll(); protected abstract void restartRadio(); protected abstract void log(String s); protected int apnTypeToId(String type) { if (TextUtils.equals(type, Phone.APN_TYPE_DEFAULT)) { return APN_DEFAULT_ID; } else if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { return APN_MMS_ID; } else if (TextUtils.equals(type, Phone.APN_TYPE_SUPL)) { return APN_SUPL_ID; } else if (TextUtils.equals(type, Phone.APN_TYPE_DUN)) { return APN_DUN_ID; } else if (TextUtils.equals(type, Phone.APN_TYPE_HIPRI)) { return APN_HIPRI_ID; } else { return APN_INVALID_ID; } } protected String apnIdToType(int id) { switch (id) { case APN_DEFAULT_ID: return Phone.APN_TYPE_DEFAULT; case APN_MMS_ID: return Phone.APN_TYPE_MMS; case APN_SUPL_ID: return Phone.APN_TYPE_SUPL; case APN_DUN_ID: return Phone.APN_TYPE_DUN; case APN_HIPRI_ID: return Phone.APN_TYPE_HIPRI; default: Log.e(LOG_TAG, "Unknown id (" + id + ") in apnIdToType"); return Phone.APN_TYPE_DEFAULT; } } protected abstract boolean isApnTypeActive(String type); protected abstract boolean isApnTypeAvailable(String type); protected abstract String[] getActiveApnTypes(); protected abstract String getActiveApnString(); public abstract ArrayList<DataConnection> getAllDataConnections(); protected abstract String getInterfaceName(String apnType); protected abstract String getIpAddress(String apnType); protected abstract String getGateway(String apnType); protected abstract String[] getDnsServers(String apnType); protected abstract void setState(State s); protected synchronized boolean isEnabled(int id) { if (id != APN_INVALID_ID) { return dataEnabled[id]; } return false; } /** * Ensure that we are connected to an APN of the specified type. * @param type the APN type (currently the only valid values * are {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}) * @return the result of the operation. Success is indicated by * a return value of either {@code Phone.APN_ALREADY_ACTIVE} or * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a broadcast * will be sent by the ConnectivityManager when a connection to * the APN has been established. */ public synchronized int enableApnType(String type) { int id = apnTypeToId(type); if (id == APN_INVALID_ID) { return Phone.APN_REQUEST_FAILED; } if (DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = " + isApnTypeActive(type) + " and state = " + state); if (!isApnTypeAvailable(type)) { if (DBG) Log.d(LOG_TAG, "type not available"); return Phone.APN_TYPE_NOT_AVAILABLE; } // just because it's active doesn't mean we had it explicitly requested before // (a broad default may handle many types). make sure we mark it enabled // so if the default is disabled we keep the connection for others setEnabled(id, true); if (isApnTypeActive(type)) { if (state == State.INITING) return Phone.APN_REQUEST_STARTED; else if (state == State.CONNECTED) return Phone.APN_ALREADY_ACTIVE; } return Phone.APN_REQUEST_STARTED; } /** * The APN of the specified type is no longer needed. Ensure that if * use of the default APN has not been explicitly disabled, we are connected * to the default APN. * @param type the APN type. The only valid values are currently * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}. * @return */ public synchronized int disableApnType(String type) { if (DBG) Log.d(LOG_TAG, "disableApnType("+type+")"); int id = apnTypeToId(type); if (id == APN_INVALID_ID) { return Phone.APN_REQUEST_FAILED; } if (isEnabled(id)) { setEnabled(id, false); if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { if (dataEnabled[APN_DEFAULT_ID]) { return Phone.APN_ALREADY_ACTIVE; } else { return Phone.APN_REQUEST_STARTED; } } else { return Phone.APN_REQUEST_STARTED; } } else { return Phone.APN_REQUEST_FAILED; } } private void setEnabled(int id, boolean enable) { if (DBG) Log.d(LOG_TAG, "setEnabled(" + id + ", " + enable + ") with old state = " + dataEnabled[id] + " and enabledCount = " + enabledCount); Message msg = obtainMessage(EVENT_ENABLE_NEW_APN); msg.arg1 = id; msg.arg2 = (enable ? ENABLED : DISABLED); sendMessage(msg); } protected synchronized void onEnableApn(int apnId, int enabled) { if (DBG) { Log.d(LOG_TAG, "EVENT_APN_ENABLE_REQUEST " + apnId + ", " + enabled); Log.d(LOG_TAG, " dataEnabled = " + dataEnabled[apnId] + ", enabledCount = " + enabledCount + ", isApnTypeActive = " + isApnTypeActive(apnIdToType(apnId))); } if (enabled == ENABLED) { if (!dataEnabled[apnId]) { dataEnabled[apnId] = true; enabledCount++; } String type = apnIdToType(apnId); if (!isApnTypeActive(type)) { mRequestedApnType = type; onEnableNewApn(); } } else { // disable if (dataEnabled[apnId]) { dataEnabled[apnId] = false; enabledCount--; if (enabledCount == 0) { onCleanUpConnection(true, Phone.REASON_DATA_DISABLED); } else if (dataEnabled[APN_DEFAULT_ID] == true && !isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { mRequestedApnType = Phone.APN_TYPE_DEFAULT; onEnableNewApn(); } } } } /** * Called when we switch APNs. * * mRequestedApnType is set prior to call * To be overridden. */ protected void onEnableNewApn() { } /** * Prevent mobile data connections from being established, * or once again allow mobile data connections. If the state * toggles, then either tear down or set up data, as * appropriate to match the new state. * <p>This operation only affects the default APN, and if the same APN is * currently being used for MMS traffic, the teardown will not happen * even when {@code enable} is {@code false}.</p> * @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data * @return {@code true} if the operation succeeded */ public boolean setDataEnabled(boolean enable) { if (DBG) Log.d(LOG_TAG, "setDataEnabled(" + enable + ")"); Message msg = obtainMessage(EVENT_SET_MASTER_DATA_ENABLE); msg.arg1 = (enable ? ENABLED : DISABLED); sendMessage(msg); return true; } protected void onSetDataEnabled(boolean enable) { if (mMasterDataEnabled != enable) { mMasterDataEnabled = enable; if (enable) { mRetryMgr.resetRetryCount(); onTrySetupData(Phone.REASON_DATA_ENABLED); } else { onCleanUpConnection(true, Phone.REASON_DATA_DISABLED); } } } }