/* * 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.dataconnection; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.DctConstants; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneBase; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.RetryManager; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import android.app.PendingIntent; import android.net.LinkCapabilities; import android.net.LinkProperties; import android.net.ProxyProperties; import android.os.AsyncResult; import android.os.Build; import android.os.Message; import android.os.SystemClock; import android.os.SystemProperties; import android.text.TextUtils; import android.telephony.Rlog; import android.telephony.TelephonyManager; import android.util.Patterns; import android.util.TimeUtils; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * {@hide} * * DataConnection StateMachine. * * This a class for representing a single data connection, with instances of this * class representing a connection via the cellular network. There may be multiple * data connections and all of them are managed by the DataConnectionTracker. * * A recent change is to move retry handling into this class, with that change the * old retry manager is now used internally rather than exposed to the DCT. Also, * bringUp now has an initialRetry which is used limit the number of retries * during the initial bring up of the connection. After the connection becomes active * the current max retry is restored to the configured value. * * NOTE: All DataConnection objects must be running on the same looper, which is the default * as the coordinator has members which are used without synchronization. */ public final class DataConnection extends StateMachine { private static final boolean DBG = true; private static final boolean VDBG = true; /** Retry configuration: A doubling of retry times from 5secs to 30minutes */ private 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 */ private static final String SECONDARY_DATA_RETRY_CONFIG = "max_retries=3, 5000, 5000, 5000"; // The data connection controller private DcController mDcController; // The Tester for failing all bringup's private DcTesterFailBringUpAll mDcTesterFailBringUpAll; private static AtomicInteger mInstanceNumber = new AtomicInteger(0); private AsyncChannel mAc; // Utilities for the DataConnection private DcRetryAlarmController mDcRetryAlarmController; // The DCT that's talking to us, we only support one! private DcTrackerBase mDct = null; /** * Used internally for saving connecting parameters. */ static class ConnectionParams { int mTag; ApnContext mApnContext; int mInitialMaxRetry; int mProfileId; Message mOnCompletedMsg; ConnectionParams(ApnContext apnContext, int initialMaxRetry, int profileId, Message onCompletedMsg) { mApnContext = apnContext; mInitialMaxRetry = initialMaxRetry; mProfileId = profileId; mOnCompletedMsg = onCompletedMsg; } @Override public String toString() { return "{mTag=" + mTag + " mApnContext=" + mApnContext + " mInitialMaxRetry=" + mInitialMaxRetry + " mProfileId=" + mProfileId + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) + "}"; } } /** * Used internally for saving disconnecting parameters. */ static class DisconnectParams { int mTag; ApnContext mApnContext; String mReason; Message mOnCompletedMsg; DisconnectParams(ApnContext apnContext, String reason, Message onCompletedMsg) { mApnContext = apnContext; mReason = reason; mOnCompletedMsg = onCompletedMsg; } @Override public String toString() { return "{mTag=" + mTag + " mApnContext=" + mApnContext + " mReason=" + mReason + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) + "}"; } } private ApnSetting mApnSetting; private ConnectionParams mConnectionParams; private DisconnectParams mDisconnectParams; private DcFailCause mDcFailCause; private PhoneBase mPhone; private LinkProperties mLinkProperties = new LinkProperties(); private LinkCapabilities mLinkCapabilities = new LinkCapabilities(); private long mCreateTime; private long mLastFailTime; private DcFailCause mLastFailCause; private static final String NULL_IP = "0.0.0.0"; private Object mUserData; //***** Package visible variables int mTag; int mCid; List mApnContexts = null; PendingIntent mReconnectIntent = null; RetryManager mRetryManager = new RetryManager(); // ***** Event codes for driving the state machine, package visible for Dcc static final int BASE = Protocol.BASE_DATA_CONNECTION; static final int EVENT_CONNECT = BASE + 0; static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1; static final int EVENT_GET_LAST_FAIL_DONE = BASE + 2; static final int EVENT_DEACTIVATE_DONE = BASE + 3; static final int EVENT_DISCONNECT = BASE + 4; static final int EVENT_RIL_CONNECTED = BASE + 5; static final int EVENT_DISCONNECT_ALL = BASE + 6; static final int EVENT_DATA_STATE_CHANGED = BASE + 7; static final int EVENT_TEAR_DOWN_NOW = BASE + 8; static final int EVENT_LOST_CONNECTION = BASE + 9; static final int EVENT_RETRY_CONNECTION = BASE + 10; private static final int CMD_TO_STRING_COUNT = EVENT_RETRY_CONNECTION - BASE + 1; private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT]; static { sCmdToString[EVENT_CONNECT - BASE] = "EVENT_CONNECT"; sCmdToString[EVENT_SETUP_DATA_CONNECTION_DONE - BASE] = "EVENT_SETUP_DATA_CONNECTION_DONE"; sCmdToString[EVENT_GET_LAST_FAIL_DONE - BASE] = "EVENT_GET_LAST_FAIL_DONE"; sCmdToString[EVENT_DEACTIVATE_DONE - BASE] = "EVENT_DEACTIVATE_DONE"; sCmdToString[EVENT_DISCONNECT - BASE] = "EVENT_DISCONNECT"; sCmdToString[EVENT_RIL_CONNECTED - BASE] = "EVENT_RIL_CONNECTED"; sCmdToString[EVENT_DISCONNECT_ALL - BASE] = "EVENT_DISCONNECT_ALL"; sCmdToString[EVENT_DATA_STATE_CHANGED - BASE] = "EVENT_DATA_STATE_CHANGED"; sCmdToString[EVENT_TEAR_DOWN_NOW - BASE] = "EVENT_TEAR_DOWN_NOW"; sCmdToString[EVENT_LOST_CONNECTION - BASE] = "EVENT_LOST_CONNECTION"; sCmdToString[EVENT_RETRY_CONNECTION - BASE] = "EVENT_RETRY_CONNECTION"; } // Convert cmd to string or null if unknown static String cmdToString(int cmd) { String value; cmd -= BASE; if ((cmd >= 0) && (cmd < sCmdToString.length)) { value = sCmdToString[cmd]; } else { value = DcAsyncChannel.cmdToString(cmd + BASE); } if (value == null) { value = "0x" + Integer.toHexString(cmd + BASE); } return value; } /** * Create the connection object * * @param phone the Phone * @param id the connection id * @return DataConnection that was created. */ static DataConnection makeDataConnection(PhoneBase phone, int id, DcTrackerBase dct, DcTesterFailBringUpAll failBringUpAll, DcController dcc) { DataConnection dc = new DataConnection(phone, "DC-" + mInstanceNumber.incrementAndGet(), id, dct, failBringUpAll, dcc); dc.start(); if (DBG) dc.log("Made " + dc.getName()); return dc; } void dispose() { log("dispose: call quiteNow()"); quitNow(); } /* Getter functions */ LinkCapabilities getCopyLinkCapabilities() { return new LinkCapabilities(mLinkCapabilities); } LinkProperties getCopyLinkProperties() { return new LinkProperties(mLinkProperties); } boolean getIsInactive() { return getCurrentState() == mInactiveState; } int getCid() { return mCid; } ApnSetting getApnSetting() { return mApnSetting; } void setLinkPropertiesHttpProxy(ProxyProperties proxy) { mLinkProperties.setHttpProxy(proxy); } static class UpdateLinkPropertyResult { public DataCallResponse.SetupResult setupResult = DataCallResponse.SetupResult.SUCCESS; public LinkProperties oldLp; public LinkProperties newLp; public UpdateLinkPropertyResult(LinkProperties curLp) { oldLp = curLp; newLp = curLp; } } UpdateLinkPropertyResult updateLinkProperty(DataCallResponse newState) { UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties); if (newState == null) return result; DataCallResponse.SetupResult setupResult; result.newLp = new LinkProperties(); // set link properties based on data call response result.setupResult = setLinkProperties(newState, result.newLp); if (result.setupResult != DataCallResponse.SetupResult.SUCCESS) { if (DBG) log("updateLinkProperty failed : " + result.setupResult); return result; } // copy HTTP proxy as it is not part DataCallResponse. result.newLp.setHttpProxy(mLinkProperties.getHttpProxy()); if (DBG && (! result.oldLp.equals(result.newLp))) { log("updateLinkProperty old LP=" + result.oldLp); log("updateLinkProperty new LP=" + result.newLp); } mLinkProperties = result.newLp; return result; } //***** Constructor (NOTE: uses dcc.getHandler() as its Handler) private DataConnection(PhoneBase phone, String name, int id, DcTrackerBase dct, DcTesterFailBringUpAll failBringUpAll, DcController dcc) { super(name, dcc.getHandler()); setLogRecSize(300); setLogOnlyTransitions(true); if (DBG) log("DataConnection constructor E"); mPhone = phone; mDct = dct; mDcTesterFailBringUpAll = failBringUpAll; mDcController = dcc; mId = id; mCid = -1; mDcRetryAlarmController = new DcRetryAlarmController(mPhone, this); addState(mDefaultState); addState(mInactiveState, mDefaultState); addState(mActivatingState, mDefaultState); addState(mRetryingState, mDefaultState); addState(mActiveState, mDefaultState); addState(mDisconnectingState, mDefaultState); addState(mDisconnectingErrorCreatingConnection, mDefaultState); setInitialState(mInactiveState); mApnContexts = new ArrayList(); if (DBG) log("DataConnection constructor X"); } private String getRetryConfig(boolean forDefault) { int nt = mPhone.getServiceState().getNetworkType(); if (Build.IS_DEBUGGABLE) { String config = SystemProperties.get("test.data_retry_config"); if (! TextUtils.isEmpty(config)) { return config; } } if ((nt == TelephonyManager.NETWORK_TYPE_CDMA) || (nt == TelephonyManager.NETWORK_TYPE_1xRTT) || (nt == TelephonyManager.NETWORK_TYPE_EVDO_0) || (nt == TelephonyManager.NETWORK_TYPE_EVDO_A) || (nt == TelephonyManager.NETWORK_TYPE_EVDO_B) || (nt == TelephonyManager.NETWORK_TYPE_EHRPD)) { // CDMA variant return SystemProperties.get("ro.cdma.data_retry_config"); } else { // Use GSM variant for all others. if (forDefault) { return SystemProperties.get("ro.gsm.data_retry_config"); } else { return SystemProperties.get("ro.gsm.2nd_data_retry_config"); } } } private void configureRetry(boolean forDefault) { String retryConfig = getRetryConfig(forDefault); if (!mRetryManager.configure(retryConfig)) { if (forDefault) { if (!mRetryManager.configure(DEFAULT_DATA_RETRY_CONFIG)) { // Should never happen, log an error and default to a simple linear sequence. loge("configureRetry: Could not configure using " + "DEFAULT_DATA_RETRY_CONFIG=" + DEFAULT_DATA_RETRY_CONFIG); mRetryManager.configure(5, 2000, 1000); } } else { if (!mRetryManager.configure(SECONDARY_DATA_RETRY_CONFIG)) { // Should never happen, log an error and default to a simple sequence. loge("configureRetry: Could note configure using " + "SECONDARY_DATA_RETRY_CONFIG=" + SECONDARY_DATA_RETRY_CONFIG); mRetryManager.configure(5, 2000, 1000); } } } if (DBG) { log("configureRetry: forDefault=" + forDefault + " mRetryManager=" + mRetryManager); } } /** * Begin setting up a data connection, calls setupDataCall * and the ConnectionParams will be returned with the * EVENT_SETUP_DATA_CONNECTION_DONE AsyncResul.userObj. * * @param cp is the connection parameters */ private void onConnect(ConnectionParams cp) { if (DBG) log("onConnect: carrier='" + mApnSetting.carrier + "' APN='" + mApnSetting.apn + "' proxy='" + mApnSetting.proxy + "' port='" + mApnSetting.port + "'"); // Check if we should fake an error. if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter > 0) { DataCallResponse response = new DataCallResponse(); response.version = mPhone.mCi.getRilVersion(); response.status = mDcTesterFailBringUpAll.getDcFailBringUp().mFailCause.getErrorCode(); response.cid = 0; response.active = 0; response.type = ""; response.ifname = ""; response.addresses = new String[0]; response.dnses = new String[0]; response.gateways = new String[0]; response.suggestedRetryTime = mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime; Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp); AsyncResult.forMessage(msg, response, null); sendMessage(msg); if (DBG) { log("onConnect: FailBringUpAll=" + mDcTesterFailBringUpAll.getDcFailBringUp() + " send error response=" + response); } mDcTesterFailBringUpAll.getDcFailBringUp().mCounter -= 1; return; } mCreateTime = -1; mLastFailTime = -1; mLastFailCause = DcFailCause.NONE; // msg.obj will be returned in AsyncResult.userObj; Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp); msg.obj = cp; int authType = mApnSetting.authType; if (authType == -1) { authType = TextUtils.isEmpty(mApnSetting.user) ? RILConstants.SETUP_DATA_AUTH_NONE : RILConstants.SETUP_DATA_AUTH_PAP_CHAP; } String protocol; if (mPhone.getServiceState().getRoaming()) { protocol = mApnSetting.roamingProtocol; } else { protocol = mApnSetting.protocol; } mPhone.mCi.setupDataCall( Integer.toString(getRilRadioTechnology()), Integer.toString(cp.mProfileId), mApnSetting.apn, mApnSetting.user, mApnSetting.password, Integer.toString(authType), protocol, msg); } /** * TearDown the data connection when the deactivation is complete a Message with * msg.what == EVENT_DEACTIVATE_DONE and msg.obj == AsyncResult with AsyncResult.obj * containing the parameter o. * * @param o is the object returned in the AsyncResult.obj. */ private void tearDownData(Object o) { int discReason = RILConstants.DEACTIVATE_REASON_NONE; if ((o != null) && (o instanceof DisconnectParams)) { DisconnectParams dp = (DisconnectParams)o; if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)) { discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF; } else if (TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) { discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET; } } if (mPhone.mCi.getRadioState().isOn()) { if (DBG) log("tearDownData radio is on, call deactivateDataCall"); mPhone.mCi.deactivateDataCall(mCid, discReason, obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o)); } else { if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately"); AsyncResult ar = new AsyncResult(o, null, null); sendMessage(obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, ar)); } } private void notifyAllWithEvent(ApnContext alreadySent, int event, String reason) { for (ApnContext apnContext : mApnContexts) { if (apnContext == alreadySent) continue; if (reason != null) apnContext.setReason(reason); Message msg = mDct.obtainMessage(event, apnContext); AsyncResult.forMessage(msg); msg.sendToTarget(); } } private void notifyAllOfConnected(String reason) { notifyAllWithEvent(null, DctConstants.EVENT_DATA_SETUP_COMPLETE, reason); } private void notifyAllOfDisconnectDcRetrying(String reason) { notifyAllWithEvent(null, DctConstants.EVENT_DISCONNECT_DC_RETRYING, reason); } private void notifyAllDisconnectCompleted(DcFailCause cause) { notifyAllWithEvent(null, DctConstants.EVENT_DISCONNECT_DONE, cause.toString()); } /** * Send the connectionCompletedMsg. * * @param cp is the ConnectionParams * @param cause and if no error the cause is DcFailCause.NONE * @param sendAll is true if all contexts are to be notified */ private void notifyConnectCompleted(ConnectionParams cp, DcFailCause cause, boolean sendAll) { ApnContext alreadySent = null; if (cp != null && cp.mOnCompletedMsg != null) { // Get the completed message but only use it once Message connectionCompletedMsg = cp.mOnCompletedMsg; cp.mOnCompletedMsg = null; if (connectionCompletedMsg.obj instanceof ApnContext) { alreadySent = (ApnContext)connectionCompletedMsg.obj; } long timeStamp = System.currentTimeMillis(); connectionCompletedMsg.arg1 = mCid; if (cause == DcFailCause.NONE) { mCreateTime = timeStamp; AsyncResult.forMessage(connectionCompletedMsg); } else { mLastFailCause = cause; mLastFailTime = timeStamp; // Return message with a Throwable exception to signify an error. if (cause == null) cause = DcFailCause.UNKNOWN; AsyncResult.forMessage(connectionCompletedMsg, cause, new Throwable(cause.toString())); } if (DBG) { log("notifyConnectCompleted at " + timeStamp + " cause=" + cause + " connectionCompletedMsg=" + msgToString(connectionCompletedMsg)); } connectionCompletedMsg.sendToTarget(); } if (sendAll) { notifyAllWithEvent(alreadySent, DctConstants.EVENT_DATA_SETUP_COMPLETE_ERROR, cause.toString()); } } /** * Send ar.userObj if its a message, which is should be back to originator. * * @param dp is the DisconnectParams. */ private void notifyDisconnectCompleted(DisconnectParams dp, boolean sendAll) { if (VDBG) log("NotifyDisconnectCompleted"); ApnContext alreadySent = null; String reason = null; if (dp != null && dp.mOnCompletedMsg != null) { // Get the completed message but only use it once Message msg = dp.mOnCompletedMsg; dp.mOnCompletedMsg = null; if (msg.obj instanceof ApnContext) { alreadySent = (ApnContext)msg.obj; } reason = dp.mReason; if (VDBG) { log(String.format("msg=%s msg.obj=%s", msg.toString(), ((msg.obj instanceof String) ? (String) msg.obj : ""))); } AsyncResult.forMessage(msg); msg.sendToTarget(); } if (sendAll) { if (reason == null) { reason = DcFailCause.UNKNOWN.toString(); } notifyAllWithEvent(alreadySent, DctConstants.EVENT_DISCONNECT_DONE, reason); } if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp); } private int getRilRadioTechnology() { int rilRadioTechnology; if (mApnSetting.bearer > 0) { rilRadioTechnology = mApnSetting.bearer + 2; } else { rilRadioTechnology = mPhone.getServiceState().getRilDataRadioTechnology() + 2; } return rilRadioTechnology; } /* * ************************************************************************** * Begin Members and methods owned by DataConnectionTracker but stored * in a DataConnection because there is one per connection. * ************************************************************************** */ /* * The id is owned by DataConnectionTracker. */ private int mId; /** * Get the DataConnection ID */ public int getDataConnectionId() { return mId; } /* * ************************************************************************** * End members owned by DataConnectionTracker * ************************************************************************** */ /** * Clear all settings called when entering mInactiveState. */ private void clearSettings() { if (DBG) log("clearSettings"); mCreateTime = -1; mLastFailTime = -1; mLastFailCause = DcFailCause.NONE; mCid = -1; mLinkProperties = new LinkProperties(); mApnContexts.clear(); mApnSetting = null; mDcFailCause = null; } /** * Process setup completion. * * @param ar is the result * @return SetupResult. */ private DataCallResponse.SetupResult onSetupConnectionCompleted(AsyncResult ar) { DataCallResponse response = (DataCallResponse) ar.result; ConnectionParams cp = (ConnectionParams) ar.userObj; DataCallResponse.SetupResult result; if (cp.mTag != mTag) { if (DBG) { log("onSetupConnectionCompleted stale cp.tag=" + cp.mTag + ", mtag=" + mTag); } result = DataCallResponse.SetupResult.ERR_Stale; } else if (ar.exception != null) { if (DBG) { log("onSetupConnectionCompleted failed, ar.exception=" + ar.exception + " response=" + response); } if (ar.exception instanceof CommandException && ((CommandException) (ar.exception)).getCommandError() == CommandException.Error.RADIO_NOT_AVAILABLE) { result = DataCallResponse.SetupResult.ERR_BadCommand; result.mFailCause = DcFailCause.RADIO_NOT_AVAILABLE; } else if ((response == null) || (response.version < 4)) { result = DataCallResponse.SetupResult.ERR_GetLastErrorFromRil; } else { result = DataCallResponse.SetupResult.ERR_RilError; result.mFailCause = DcFailCause.fromInt(response.status); } } else if (response.status != 0) { result = DataCallResponse.SetupResult.ERR_RilError; result.mFailCause = DcFailCause.fromInt(response.status); } else { if (DBG) log("onSetupConnectionCompleted received DataCallResponse: " + response); mCid = response.cid; result = updateLinkProperty(response).setupResult; } return result; } private boolean isDnsOk(String[] domainNameServers) { if (NULL_IP.equals(domainNameServers[0]) && NULL_IP.equals(domainNameServers[1]) && !mPhone.isDnsCheckDisabled()) { // Work around a race condition where QMI does not fill in DNS: // Deactivate PDP and let DataConnectionTracker retry. // Do not apply the race condition workaround for MMS APN // if Proxy is an IP-address. // Otherwise, the default APN will not be restored anymore. if (!mApnSetting.types[0].equals(PhoneConstants.APN_TYPE_MMS) || !isIpAddress(mApnSetting.mmsProxy)) { log(String.format( "isDnsOk: return false apn.types[0]=%s APN_TYPE_MMS=%s isIpAddress(%s)=%s", mApnSetting.types[0], PhoneConstants.APN_TYPE_MMS, mApnSetting.mmsProxy, isIpAddress(mApnSetting.mmsProxy))); return false; } } return true; } private boolean isIpAddress(String address) { if (address == null) return false; return Patterns.IP_ADDRESS.matcher(address).matches(); } private DataCallResponse.SetupResult setLinkProperties(DataCallResponse response, LinkProperties lp) { // Check if system property dns usable boolean okToUseSystemPropertyDns = false; String propertyPrefix = "net." + response.ifname + "."; String dnsServers[] = new String[2]; dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1"); dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2"); okToUseSystemPropertyDns = isDnsOk(dnsServers); // set link properties based on data call response return response.setLinkProperties(lp, okToUseSystemPropertyDns); } /** * Initialize connection, this will fail if the * apnSettings are not compatible. * * @param cp the Connection paramemters * @return true if initialization was successful. */ private boolean initConnection(ConnectionParams cp) { ApnContext apnContext = cp.mApnContext; if (mApnSetting == null) { // Only change apn setting if it isn't set, it will // only NOT be set only if we're in DcInactiveState. mApnSetting = apnContext.getApnSetting(); } else if (mApnSetting.canHandleType(apnContext.getApnType())) { // All is good. } else { if (DBG) { log("initConnection: incompatible apnSetting in ConnectionParams cp=" + cp + " dc=" + DataConnection.this); } return false; } mTag += 1; mConnectionParams = cp; mConnectionParams.mTag = mTag; if (!mApnContexts.contains(apnContext)) { mApnContexts.add(apnContext); } configureRetry(mApnSetting.canHandleType(PhoneConstants.APN_TYPE_DEFAULT)); mRetryManager.setRetryCount(0); mRetryManager.setCurMaxRetryCount(mConnectionParams.mInitialMaxRetry); mRetryManager.setRetryForever(false); if (DBG) { log("initConnection: " + " RefCount=" + mApnContexts.size() + " mApnList=" + mApnContexts + " mConnectionParams=" + mConnectionParams); } return true; } /** * The parent state for all other states. */ private class DcDefaultState extends State { @Override public void enter() { if (DBG) log("DcDefaultState: enter"); // Add ourselves to the list of data connections mDcController.addDc(DataConnection.this); } @Override public void exit() { if (DBG) log("DcDefaultState: exit"); if (mAc != null) { mAc.disconnected(); mAc = null; } mDcRetryAlarmController.dispose(); mDcRetryAlarmController = null; mApnContexts = null; mReconnectIntent = null; mDct = null; mApnSetting = null; mPhone = null; mLinkProperties = null; mLinkCapabilities = null; mLastFailCause = null; mUserData = null; // Remove ourselves from the DC lists mDcController.removeDc(DataConnection.this); mDcController = null; mDcTesterFailBringUpAll = null; } @Override public boolean processMessage(Message msg) { boolean retVal = HANDLED; AsyncResult ar; if (VDBG) { log("DcDefault msg=" + getWhatToString(msg.what) + " RefCount=" + mApnContexts.size()); } switch (msg.what) { case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { if (mAc != null) { if (VDBG) log("Disconnecting to previous connection mAc=" + mAc); mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED); } else { mAc = new AsyncChannel(); mAc.connected(null, getHandler(), msg.replyTo); if (VDBG) log("DcDefaultState: FULL_CONNECTION reply connected"); mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, AsyncChannel.STATUS_SUCCESSFUL, mId, "hi"); } break; } case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { if (VDBG) log("CMD_CHANNEL_DISCONNECTED"); quit(); break; } case DcAsyncChannel.REQ_IS_INACTIVE: { boolean val = getIsInactive(); if (VDBG) log("REQ_IS_INACTIVE isInactive=" + val); mAc.replyToMessage(msg, DcAsyncChannel.RSP_IS_INACTIVE, val ? 1 : 0); break; } case DcAsyncChannel.REQ_GET_CID: { int cid = getCid(); if (VDBG) log("REQ_GET_CID cid=" + cid); mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_CID, cid); break; } case DcAsyncChannel.REQ_GET_APNSETTING: { ApnSetting apnSetting = getApnSetting(); if (VDBG) log("REQ_GET_APNSETTING mApnSetting=" + apnSetting); mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_APNSETTING, apnSetting); break; } case DcAsyncChannel.REQ_GET_LINK_PROPERTIES: { LinkProperties lp = getCopyLinkProperties(); if (VDBG) log("REQ_GET_LINK_PROPERTIES linkProperties" + lp); mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_LINK_PROPERTIES, lp); break; } case DcAsyncChannel.REQ_SET_LINK_PROPERTIES_HTTP_PROXY: { ProxyProperties proxy = (ProxyProperties) msg.obj; if (VDBG) log("REQ_SET_LINK_PROPERTIES_HTTP_PROXY proxy=" + proxy); setLinkPropertiesHttpProxy(proxy); mAc.replyToMessage(msg, DcAsyncChannel.RSP_SET_LINK_PROPERTIES_HTTP_PROXY); break; } case DcAsyncChannel.REQ_GET_LINK_CAPABILITIES: { LinkCapabilities lc = getCopyLinkCapabilities(); if (VDBG) log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc); mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_LINK_CAPABILITIES, lc); break; } case DcAsyncChannel.REQ_RESET: if (VDBG) log("DcDefaultState: msg.what=REQ_RESET"); transitionTo(mInactiveState); break; case EVENT_CONNECT: if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected"); ConnectionParams cp = (ConnectionParams) msg.obj; notifyConnectCompleted(cp, DcFailCause.UNKNOWN, false); break; case EVENT_DISCONNECT: if (DBG) { log("DcDefaultState deferring msg.what=EVENT_DISCONNECT RefCount=" + mApnContexts.size()); } deferMessage(msg); break; case EVENT_DISCONNECT_ALL: if (DBG) { log("DcDefaultState deferring msg.what=EVENT_DISCONNECT_ALL RefCount=" + mApnContexts.size()); } deferMessage(msg); break; case EVENT_TEAR_DOWN_NOW: if (DBG) log("DcDefaultState EVENT_TEAR_DOWN_NOW"); mPhone.mCi.deactivateDataCall(mCid, 0, null); break; case EVENT_LOST_CONNECTION: if (DBG) { String s = "DcDefaultState ignore EVENT_LOST_CONNECTION" + " tag=" + msg.arg1 + ":mTag=" + mTag; logAndAddLogRec(s); } break; case EVENT_RETRY_CONNECTION: if (DBG) { String s = "DcDefaultState ignore EVENT_RETRY_CONNECTION" + " tag=" + msg.arg1 + ":mTag=" + mTag; logAndAddLogRec(s); } break; default: if (DBG) { log("DcDefaultState: shouldn't happen but ignore msg.what=" + getWhatToString(msg.what)); } break; } return retVal; } } private DcDefaultState mDefaultState = new DcDefaultState(); /** * The state machine is inactive and expects a EVENT_CONNECT. */ private class DcInactiveState extends State { // Inform all contexts we've failed connecting public void setEnterNotificationParams(ConnectionParams cp, DcFailCause cause) { if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause"); mConnectionParams = cp; mDisconnectParams = null; mDcFailCause = cause; } // Inform all contexts we've failed disconnected public void setEnterNotificationParams(DisconnectParams dp) { if (VDBG) log("DcInactiveState: setEnterNoticationParams dp"); mConnectionParams = null; mDisconnectParams = dp; mDcFailCause = DcFailCause.NONE; } // Inform all contexts of the failure cause public void setEnterNotificationParams(DcFailCause cause) { mConnectionParams = null; mDisconnectParams = null; mDcFailCause = cause; } @Override public void enter() { mTag += 1; if (DBG) log("DcInactiveState: enter() mTag=" + mTag); if (mConnectionParams != null) { if (DBG) { log("DcInactiveState: enter notifyConnectCompleted +ALL failCause=" + mDcFailCause); } notifyConnectCompleted(mConnectionParams, mDcFailCause, true); } if (mDisconnectParams != null) { if (DBG) { log("DcInactiveState: enter notifyDisconnectCompleted +ALL failCause=" + mDcFailCause); } notifyDisconnectCompleted(mDisconnectParams, true); } if (mDisconnectParams == null && mConnectionParams == null && mDcFailCause != null) { if (DBG) { log("DcInactiveState: enter notifyAllDisconnectCompleted failCause=" + mDcFailCause); } notifyAllDisconnectCompleted(mDcFailCause); } // Remove ourselves from cid mapping, before clearSettings mDcController.removeActiveDcByCid(DataConnection.this); clearSettings(); } @Override public void exit() { } @Override public boolean processMessage(Message msg) { boolean retVal; switch (msg.what) { case DcAsyncChannel.REQ_RESET: if (DBG) { log("DcInactiveState: msg.what=RSP_RESET, ignore we're already reset"); } retVal = HANDLED; break; case EVENT_CONNECT: if (DBG) log("DcInactiveState: mag.what=EVENT_CONNECT"); ConnectionParams cp = (ConnectionParams) msg.obj; if (initConnection(cp)) { onConnect(mConnectionParams); transitionTo(mActivatingState); } else { if (DBG) { log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed"); } notifyConnectCompleted(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER, false); } retVal = HANDLED; break; case EVENT_DISCONNECT: if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT"); notifyDisconnectCompleted((DisconnectParams)msg.obj, false); retVal = HANDLED; break; case EVENT_DISCONNECT_ALL: if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT_ALL"); notifyDisconnectCompleted((DisconnectParams)msg.obj, false); retVal = HANDLED; break; default: if (VDBG) { log("DcInactiveState nothandled msg.what=" + getWhatToString(msg.what)); } retVal = NOT_HANDLED; break; } return retVal; } } private DcInactiveState mInactiveState = new DcInactiveState(); /** * The state machine is retrying and expects a EVENT_RETRY_CONNECTION. */ private class DcRetryingState extends State { @Override public void enter() { if (DBG) { log("DcRetryingState: enter() mTag=" + mTag + ", call notifyAllOfDisconnectDcRetrying lostConnection"); } notifyAllOfDisconnectDcRetrying(Phone.REASON_LOST_DATA_CONNECTION); // Remove ourselves from cid mapping mDcController.removeActiveDcByCid(DataConnection.this); mCid = -1; } @Override public boolean processMessage(Message msg) { boolean retVal; switch (msg.what) { case EVENT_RETRY_CONNECTION: { if (msg.arg1 == mTag) { mRetryManager.increaseRetryCount(); if (DBG) { log("DcRetryingState EVENT_RETRY_CONNECTION" + " RetryCount=" + mRetryManager.getRetryCount() + " mConnectionParams=" + mConnectionParams); } onConnect(mConnectionParams); transitionTo(mActivatingState); } else { if (DBG) { log("DcRetryingState stale EVENT_RETRY_CONNECTION" + " tag:" + msg.arg1 + " != mTag:" + mTag); } } retVal = HANDLED; break; } case DcAsyncChannel.REQ_RESET: { if (DBG) { log("DcRetryingState: msg.what=RSP_RESET, ignore we're already reset"); } mInactiveState.setEnterNotificationParams(mConnectionParams, DcFailCause.RESET_BY_FRAMEWORK); transitionTo(mInactiveState); retVal = HANDLED; break; } case EVENT_CONNECT: { ConnectionParams cp = (ConnectionParams) msg.obj; if (DBG) { log("DcRetryingState: msg.what=EVENT_CONNECT" + " RefCount=" + mApnContexts.size() + " cp=" + cp + " mConnectionParams=" + mConnectionParams); } if (initConnection(cp)) { onConnect(mConnectionParams); transitionTo(mActivatingState); } else { if (DBG) { log("DcRetryingState: msg.what=EVENT_CONNECT initConnection failed"); } notifyConnectCompleted(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER, false); } retVal = HANDLED; break; } case EVENT_DISCONNECT: { DisconnectParams dp = (DisconnectParams) msg.obj; if (mApnContexts.remove(dp.mApnContext) && mApnContexts.size() == 0) { if (DBG) { log("DcRetryingState msg.what=EVENT_DISCONNECT " + " RefCount=" + mApnContexts.size() + " dp=" + dp); } mInactiveState.setEnterNotificationParams(dp); transitionTo(mInactiveState); } else { if (DBG) log("DcRetryingState: msg.what=EVENT_DISCONNECT"); notifyDisconnectCompleted(dp, false); } retVal = HANDLED; break; } case EVENT_DISCONNECT_ALL: { if (DBG) { log("DcRetryingState msg.what=EVENT_DISCONNECT/DISCONNECT_ALL " + "RefCount=" + mApnContexts.size()); } mInactiveState.setEnterNotificationParams(DcFailCause.LOST_CONNECTION); deferMessage(msg); transitionTo(mInactiveState); retVal = HANDLED; break; } default: { if (VDBG) { log("DcRetryingState nothandled msg.what=" + getWhatToString(msg.what)); } retVal = NOT_HANDLED; break; } } return retVal; } } private DcRetryingState mRetryingState = new DcRetryingState(); /** * The state machine is activating a connection. */ private class DcActivatingState extends State { @Override public boolean processMessage(Message msg) { boolean retVal; AsyncResult ar; ConnectionParams cp; if (DBG) log("DcActivatingState: msg=" + msgToString(msg)); switch (msg.what) { case EVENT_CONNECT: deferMessage(msg); retVal = HANDLED; break; case EVENT_SETUP_DATA_CONNECTION_DONE: ar = (AsyncResult) msg.obj; cp = (ConnectionParams) ar.userObj; DataCallResponse.SetupResult result = onSetupConnectionCompleted(ar); if (result != DataCallResponse.SetupResult.ERR_Stale) { if (mConnectionParams != cp) { loge("DcActivatingState: WEIRD mConnectionsParams:"+ mConnectionParams + " != cp:" + cp); } } if (DBG) { log("DcActivatingState onSetupConnectionCompleted result=" + result + " dc=" + DataConnection.this); } switch (result) { case SUCCESS: // All is well mDcFailCause = DcFailCause.NONE; transitionTo(mActiveState); break; case ERR_BadCommand: // Vendor ril rejected the command and didn't connect. // Transition to inactive but send notifications after // we've entered the mInactive state. mInactiveState.setEnterNotificationParams(cp, result.mFailCause); transitionTo(mInactiveState); break; case ERR_UnacceptableParameter: // The addresses given from the RIL are bad tearDownData(cp); transitionTo(mDisconnectingErrorCreatingConnection); break; case ERR_GetLastErrorFromRil: // Request failed and this is an old RIL mPhone.mCi.getLastDataCallFailCause( obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp)); break; case ERR_RilError: int delay = mDcRetryAlarmController.getSuggestedRetryTime( DataConnection.this, ar); if (DBG) { log("DcActivatingState: ERR_RilError " + " delay=" + delay + " isRetryNeeded=" + mRetryManager.isRetryNeeded()); } if (delay >= 0) { mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION, mTag, delay); transitionTo(mRetryingState); } else { mInactiveState.setEnterNotificationParams(cp, result.mFailCause); transitionTo(mInactiveState); } break; case ERR_Stale: loge("DcActivatingState: stale EVENT_SETUP_DATA_CONNECTION_DONE" + " tag:" + cp.mTag + " != mTag:" + mTag); break; default: throw new RuntimeException("Unknown SetupResult, should not happen"); } retVal = HANDLED; break; case EVENT_GET_LAST_FAIL_DONE: ar = (AsyncResult) msg.obj; cp = (ConnectionParams) ar.userObj; if (cp.mTag == mTag) { if (mConnectionParams != cp) { loge("DcActivatingState: WEIRD mConnectionsParams:" + mConnectionParams + " != cp:" + cp); } DcFailCause cause = DcFailCause.UNKNOWN; if (ar.exception == null) { int rilFailCause = ((int[]) (ar.result))[0]; cause = DcFailCause.fromInt(rilFailCause); } mDcFailCause = cause; int retryDelay = mRetryManager.getRetryTimer(); if (DBG) { log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE" + " cause=" + cause + " retryDelay=" + retryDelay + " isRetryNeeded=" + mRetryManager.isRetryNeeded() + " dc=" + DataConnection.this); } if (mRetryManager.isRetryNeeded()) { mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION, mTag, retryDelay); transitionTo(mRetryingState); } else { // Transition to inactive but send notifications after // we've entered the mInactive state. mInactiveState.setEnterNotificationParams(cp, cause); transitionTo(mInactiveState); } } else { loge("DcActivatingState: stale EVENT_GET_LAST_FAIL_DONE" + " tag:" + cp.mTag + " != mTag:" + mTag); } retVal = HANDLED; break; default: if (VDBG) { log("DcActivatingState not handled msg.what=" + getWhatToString(msg.what) + " RefCount=" + mApnContexts.size()); } retVal = NOT_HANDLED; break; } return retVal; } } private DcActivatingState mActivatingState = new DcActivatingState(); /** * The state machine is connected, expecting an EVENT_DISCONNECT. */ private class DcActiveState extends State { @Override public void enter() { if (DBG) log("DcActiveState: enter dc=" + DataConnection.this); if (mRetryManager.getRetryCount() != 0) { log("DcActiveState: connected after retrying call notifyAllOfConnected"); mRetryManager.setRetryCount(0); } // If we were retrying there maybe more than one, otherwise they'll only be one. notifyAllOfConnected(Phone.REASON_CONNECTED); // If the EVENT_CONNECT set the current max retry restore it here // if it didn't then this is effectively a NOP. mRetryManager.restoreCurMaxRetryCount(); mDcController.addActiveDcByCid(DataConnection.this); } @Override public void exit() { if (DBG) log("DcActiveState: exit dc=" + this); } @Override public boolean processMessage(Message msg) { boolean retVal; switch (msg.what) { case EVENT_CONNECT: { ConnectionParams cp = (ConnectionParams) msg.obj; if (DBG) { log("DcActiveState: EVENT_CONNECT cp=" + cp + " dc=" + DataConnection.this); } if (mApnContexts.contains(cp.mApnContext)) { log("DcActiveState ERROR already added apnContext=" + cp.mApnContext); } else { mApnContexts.add(cp.mApnContext); if (DBG) { log("DcActiveState msg.what=EVENT_CONNECT RefCount=" + mApnContexts.size()); } } notifyConnectCompleted(cp, DcFailCause.NONE, false); retVal = HANDLED; break; } case EVENT_DISCONNECT: { DisconnectParams dp = (DisconnectParams) msg.obj; if (DBG) { log("DcActiveState: EVENT_DISCONNECT dp=" + dp + " dc=" + DataConnection.this); } if (mApnContexts.contains(dp.mApnContext)) { if (DBG) { log("DcActiveState msg.what=EVENT_DISCONNECT RefCount=" + mApnContexts.size()); } if (mApnContexts.size() == 1) { mApnContexts.clear(); mDisconnectParams = dp; mConnectionParams = null; dp.mTag = mTag; tearDownData(dp); transitionTo(mDisconnectingState); } else { mApnContexts.remove(dp.mApnContext); notifyDisconnectCompleted(dp, false); } } else { log("DcActiveState ERROR no such apnContext=" + dp.mApnContext + " in this dc=" + DataConnection.this); notifyDisconnectCompleted(dp, false); } retVal = HANDLED; break; } case EVENT_DISCONNECT_ALL: { if (DBG) { log("DcActiveState EVENT_DISCONNECT clearing apn contexts," + " dc=" + DataConnection.this); } mApnContexts.clear(); DisconnectParams dp = (DisconnectParams) msg.obj; mDisconnectParams = dp; mConnectionParams = null; dp.mTag = mTag; tearDownData(dp); transitionTo(mDisconnectingState); retVal = HANDLED; break; } case EVENT_LOST_CONNECTION: { if (DBG) { log("DcActiveState EVENT_LOST_CONNECTION dc=" + DataConnection.this); } if (mRetryManager.isRetryNeeded()) { // We're going to retry int delayMillis = mRetryManager.getRetryTimer(); if (DBG) { log("DcActiveState EVENT_LOST_CONNECTION startRetryAlarm" + " mTag=" + mTag + " delay=" + delayMillis + "ms"); } mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION, mTag, delayMillis); transitionTo(mRetryingState); } else { mInactiveState.setEnterNotificationParams(DcFailCause.LOST_CONNECTION); transitionTo(mInactiveState); } retVal = HANDLED; break; } default: if (VDBG) { log("DcActiveState not handled msg.what=" + getWhatToString(msg.what)); } retVal = NOT_HANDLED; break; } return retVal; } } private DcActiveState mActiveState = new DcActiveState(); /** * The state machine is disconnecting. */ private class DcDisconnectingState extends State { @Override public boolean processMessage(Message msg) { boolean retVal; switch (msg.what) { case EVENT_CONNECT: if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = " + mApnContexts.size()); deferMessage(msg); retVal = HANDLED; break; case EVENT_DEACTIVATE_DONE: if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount=" + mApnContexts.size()); AsyncResult ar = (AsyncResult) msg.obj; DisconnectParams dp = (DisconnectParams) ar.userObj; if (dp.mTag == mTag) { // Transition to inactive but send notifications after // we've entered the mInactive state. mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj); transitionTo(mInactiveState); } else { if (DBG) log("DcDisconnectState stale EVENT_DEACTIVATE_DONE" + " dp.tag=" + dp.mTag + " mTag=" + mTag); } retVal = HANDLED; break; default: if (VDBG) { log("DcDisconnectingState not handled msg.what=" + getWhatToString(msg.what)); } retVal = NOT_HANDLED; break; } return retVal; } } private DcDisconnectingState mDisconnectingState = new DcDisconnectingState(); /** * The state machine is disconnecting after an creating a connection. */ private class DcDisconnectionErrorCreatingConnection extends State { @Override public boolean processMessage(Message msg) { boolean retVal; switch (msg.what) { case EVENT_DEACTIVATE_DONE: AsyncResult ar = (AsyncResult) msg.obj; ConnectionParams cp = (ConnectionParams) ar.userObj; if (cp.mTag == mTag) { if (DBG) { log("DcDisconnectionErrorCreatingConnection" + " msg.what=EVENT_DEACTIVATE_DONE"); } // Transition to inactive but send notifications after // we've entered the mInactive state. mInactiveState.setEnterNotificationParams(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER); transitionTo(mInactiveState); } else { if (DBG) { log("DcDisconnectionErrorCreatingConnection stale EVENT_DEACTIVATE_DONE" + " dp.tag=" + cp.mTag + ", mTag=" + mTag); } } retVal = HANDLED; break; default: if (VDBG) { log("DcDisconnectionErrorCreatingConnection not handled msg.what=" + getWhatToString(msg.what)); } retVal = NOT_HANDLED; break; } return retVal; } } private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection = new DcDisconnectionErrorCreatingConnection(); // ******* "public" interface /** * Used for testing purposes. */ /* package */ void tearDownNow() { if (DBG) log("tearDownNow()"); sendMessage(obtainMessage(EVENT_TEAR_DOWN_NOW)); } /** * @return the string for msg.what as our info. */ @Override protected String getWhatToString(int what) { return cmdToString(what); } private static String msgToString(Message msg) { String retVal; if (msg == null) { retVal = "null"; } else { StringBuilder b = new StringBuilder(); b.append("{what="); b.append(cmdToString(msg.what)); b.append(" when="); TimeUtils.formatDuration(msg.getWhen() - SystemClock.uptimeMillis(), b); if (msg.arg1 != 0) { b.append(" arg1="); b.append(msg.arg1); } if (msg.arg2 != 0) { b.append(" arg2="); b.append(msg.arg2); } if (msg.obj != null) { b.append(" obj="); b.append(msg.obj); } b.append(" target="); b.append(msg.getTarget()); b.append(" replyTo="); b.append(msg.replyTo); b.append("}"); retVal = b.toString(); } return retVal; } static void slog(String s) { Rlog.d("DC", s); } /** * Log with debug * * @param s is string log */ @Override protected void log(String s) { Rlog.d(getName(), s); } /** * Log with debug attribute * * @param s is string log */ @Override protected void logd(String s) { Rlog.d(getName(), s); } /** * Log with verbose attribute * * @param s is string log */ @Override protected void logv(String s) { Rlog.v(getName(), s); } /** * Log with info attribute * * @param s is string log */ @Override protected void logi(String s) { Rlog.i(getName(), s); } /** * Log with warning attribute * * @param s is string log */ @Override protected void logw(String s) { Rlog.w(getName(), s); } /** * Log with error attribute * * @param s is string log */ @Override protected void loge(String s) { Rlog.e(getName(), s); } /** * Log with error attribute * * @param s is string log * @param e is a Throwable which logs additional information. */ @Override protected void loge(String s, Throwable e) { Rlog.e(getName(), s, e); } /** Doesn't print mApnList of ApnContext's which would be recursive */ public String toStringSimple() { return getName() + ": State=" + getCurrentState().getName() + " mApnSetting=" + mApnSetting + " RefCount=" + mApnContexts.size() + " mCid=" + mCid + " mCreateTime=" + mCreateTime + " mLastastFailTime=" + mLastFailTime + " mLastFailCause=" + mLastFailCause + " mTag=" + mTag + " mRetryManager=" + mRetryManager + " mLinkProperties=" + mLinkProperties + " mLinkCapabilities=" + mLinkCapabilities; } @Override public String toString() { return "{" + toStringSimple() + " mApnContexts=" + mApnContexts + "}"; } /** * Dump the current state. * * @param fd * @param pw * @param args */ @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.print("DataConnection "); super.dump(fd, pw, args); pw.println(" mApnContexts.size=" + mApnContexts.size()); pw.println(" mApnContexts=" + mApnContexts); pw.flush(); pw.println(" mDataConnectionTracker=" + mDct); pw.println(" mApnSetting=" + mApnSetting); pw.println(" mTag=" + mTag); pw.println(" mCid=" + mCid); pw.println(" mRetryManager=" + mRetryManager); pw.println(" mConnectionParams=" + mConnectionParams); pw.println(" mDisconnectParams=" + mDisconnectParams); pw.println(" mDcFailCause=" + mDcFailCause); pw.flush(); pw.println(" mPhone=" + mPhone); pw.flush(); pw.println(" mLinkProperties=" + mLinkProperties); pw.flush(); pw.println(" mLinkCapabilities=" + mLinkCapabilities); pw.println(" mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime)); pw.println(" mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime)); pw.println(" mLastFailCause=" + mLastFailCause); pw.flush(); pw.println(" mUserData=" + mUserData); pw.println(" mInstanceNumber=" + mInstanceNumber); pw.println(" mAc=" + mAc); pw.println(" mDcRetryAlarmController=" + mDcRetryAlarmController); pw.flush(); } }