| /* |
| * 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 com.android.internal.telephony.gsm.ApnSetting; |
| |
| import com.android.internal.util.HierarchicalState; |
| import com.android.internal.util.HierarchicalStateMachine; |
| |
| import android.net.LinkAddress; |
| import android.net.LinkCapabilities; |
| import android.net.LinkProperties; |
| import android.os.AsyncResult; |
| import android.os.Message; |
| import android.os.SystemProperties; |
| import android.util.EventLog; |
| |
| import java.net.InetAddress; |
| import java.net.InterfaceAddress; |
| import java.net.NetworkInterface; |
| import java.net.SocketException; |
| import java.net.UnknownHostException; |
| |
| /** |
| * {@hide} |
| * |
| * DataConnection HierarchicalStateMachine. |
| * |
| * This is an abstract base class for representing a single data connection. |
| * Instances of this class such as <code>CdmaDataConnection</code> and |
| * <code>GsmDataConnection</code>, * represent a connection via the cellular network. |
| * There may be multiple data connections and all of them are managed by the |
| * <code>DataConnectionTracker</code>. |
| * |
| * Instances are asynchronous state machines and have two primary entry points |
| * <code>connect()</code> and <code>disconnect</code>. The message a parameter will be returned |
| * hen the operation completes. The <code>msg.obj</code> will contain an AsyncResult |
| * object and <code>AsyncResult.userObj</code> is the original <code>msg.obj</code>. if successful |
| * with the <code>AsyncResult.result == null</code> and <code>AsyncResult.exception == null</code>. |
| * If an error <code>AsyncResult.result = FailCause</code> and |
| * <code>AsyncResult.exception = new Exception()</code>. |
| * |
| * The other public methods are provided for debugging. |
| * |
| * Below is the state machine description for this class. |
| * |
| * DataConnection { |
| * + mDefaultState { |
| * EVENT_RESET { clearSettings, notifiyDisconnectCompleted, >mInactiveState }. |
| * EVENT_CONNECT { notifyConnectCompleted(FailCause.UNKNOWN) }. |
| * EVENT_DISCONNECT { notifyDisconnectCompleted }. |
| * |
| * // Ignored messages |
| * EVENT_SETUP_DATA_CONNECTION_DONE, |
| * EVENT_GET_LAST_FAIL_DONE, |
| * EVENT_DEACTIVATE_DONE. |
| * } |
| * ++ # mInactiveState |
| * e(doNotifications) |
| * x(clearNotifications) { |
| * EVENT_RESET { notifiyDisconnectCompleted }. |
| * EVENT_CONNECT {startConnecting, >mActivatingState }. |
| * } |
| * ++ mActivatingState { |
| * EVENT_DISCONNECT { %EVENT_DISCONNECT }. |
| * EVENT_SETUP_DATA_CONNECTION_DONE { |
| * if (SUCCESS) { notifyConnectCompleted(FailCause.NONE), >mActiveState }. |
| * if (ERR_BadCommand) { |
| * notifyConnectCompleted(FailCause.UNKNOWN), >mInactiveState }. |
| * if (ERR_BadDns) { tearDownData($DEACTIVATE_DONE), >mDisconnectingBadDnsState }. |
| * if (ERR_Other) { getLastDataCallFailCause($EVENT_GET_LAST_FAIL_DONE) }. |
| * if (ERR_Stale) {}. |
| * } |
| * EVENT_GET_LAST_FAIL_DONE { notifyConnectCompleted(result), >mInactive }. |
| * } |
| * ++ mActiveState { |
| * EVENT_DISCONNECT { tearDownData($EVENT_DEACTIVATE_DONE), >mDisconnecting }. |
| * } |
| * ++ mDisconnectingState { |
| * EVENT_DEACTIVATE_DONE { notifyDisconnectCompleted, >mInactiveState }. |
| * } |
| * ++ mDisconnectingBadDnsState { |
| * EVENT_DEACTIVATE_DONE { notifyConnectComplete(FailCause.UNKNOWN), >mInactiveState }. |
| * } |
| * } |
| */ |
| public abstract class DataConnection extends HierarchicalStateMachine { |
| protected static final boolean DBG = true; |
| |
| protected static Object mCountLock = new Object(); |
| protected static int mCount; |
| |
| /** |
| * Class returned by onSetupConnectionCompleted. |
| */ |
| protected enum SetupResult { |
| ERR_BadCommand, |
| ERR_BadDns, |
| ERR_Other, |
| ERR_Stale, |
| SUCCESS; |
| |
| public FailCause mFailCause; |
| |
| @Override |
| public String toString() { |
| switch (this) { |
| case ERR_BadCommand: return "Bad Command"; |
| case ERR_BadDns: return "Bad DNS"; |
| case ERR_Other: return "Other error"; |
| case ERR_Stale: return "Stale command"; |
| case SUCCESS: return "SUCCESS"; |
| default: return "unknown"; |
| } |
| } |
| } |
| |
| /** |
| * Used internally for saving connecting parameters. |
| */ |
| protected static class ConnectionParams { |
| public ConnectionParams(ApnSetting apn, Message onCompletedMsg) { |
| this.apn = apn; |
| this.onCompletedMsg = onCompletedMsg; |
| } |
| |
| public int tag; |
| public ApnSetting apn; |
| public Message onCompletedMsg; |
| } |
| |
| /** |
| * An instance used for notification of blockingReset. |
| * TODO: Remove when blockingReset is removed. |
| */ |
| class ResetSynchronouslyLock { |
| } |
| |
| /** |
| * Used internally for saving disconnecting parameters. |
| */ |
| protected static class DisconnectParams { |
| public DisconnectParams(Message onCompletedMsg) { |
| this.onCompletedMsg = onCompletedMsg; |
| } |
| public DisconnectParams(ResetSynchronouslyLock lockObj) { |
| this.lockObj = lockObj; |
| } |
| |
| public int tag; |
| public Message onCompletedMsg; |
| public ResetSynchronouslyLock lockObj; |
| } |
| |
| /** |
| * Returned as the reason for a connection failure. |
| */ |
| public enum FailCause { |
| NONE, |
| OPERATOR_BARRED, |
| INSUFFICIENT_RESOURCES, |
| MISSING_UNKNOWN_APN, |
| UNKNOWN_PDP_ADDRESS, |
| USER_AUTHENTICATION, |
| ACTIVATION_REJECT_GGSN, |
| ACTIVATION_REJECT_UNSPECIFIED, |
| SERVICE_OPTION_NOT_SUPPORTED, |
| SERVICE_OPTION_NOT_SUBSCRIBED, |
| SERVICE_OPTION_OUT_OF_ORDER, |
| NSAPI_IN_USE, |
| PROTOCOL_ERRORS, |
| REGISTRATION_FAIL, |
| GPRS_REGISTRATION_FAIL, |
| UNKNOWN, |
| |
| RADIO_NOT_AVAILABLE; |
| |
| public boolean isPermanentFail() { |
| return (this == OPERATOR_BARRED) || (this == MISSING_UNKNOWN_APN) || |
| (this == UNKNOWN_PDP_ADDRESS) || (this == USER_AUTHENTICATION) || |
| (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) || |
| (this == SERVICE_OPTION_NOT_SUPPORTED) || |
| (this == SERVICE_OPTION_NOT_SUBSCRIBED) || (this == NSAPI_IN_USE) || |
| (this == PROTOCOL_ERRORS); |
| } |
| |
| public boolean isEventLoggable() { |
| return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) || |
| (this == UNKNOWN_PDP_ADDRESS) || (this == USER_AUTHENTICATION) || |
| (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) || |
| (this == SERVICE_OPTION_NOT_SUBSCRIBED) || |
| (this == SERVICE_OPTION_NOT_SUPPORTED) || |
| (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) || |
| (this == PROTOCOL_ERRORS); |
| } |
| |
| @Override |
| public String toString() { |
| switch (this) { |
| case NONE: |
| return "No Error"; |
| case OPERATOR_BARRED: |
| return "Operator Barred"; |
| case INSUFFICIENT_RESOURCES: |
| return "Insufficient Resources"; |
| case MISSING_UNKNOWN_APN: |
| return "Missing / Unknown APN"; |
| case UNKNOWN_PDP_ADDRESS: |
| return "Unknown PDP Address"; |
| case USER_AUTHENTICATION: |
| return "Error User Authentication"; |
| case ACTIVATION_REJECT_GGSN: |
| return "Activation Reject GGSN"; |
| case ACTIVATION_REJECT_UNSPECIFIED: |
| return "Activation Reject unspecified"; |
| case SERVICE_OPTION_NOT_SUPPORTED: |
| return "Data Not Supported"; |
| case SERVICE_OPTION_NOT_SUBSCRIBED: |
| return "Data Not subscribed"; |
| case SERVICE_OPTION_OUT_OF_ORDER: |
| return "Data Services Out of Order"; |
| case NSAPI_IN_USE: |
| return "NSAPI in use"; |
| case PROTOCOL_ERRORS: |
| return "Protocol Errors"; |
| case REGISTRATION_FAIL: |
| return "Network Registration Failure"; |
| case GPRS_REGISTRATION_FAIL: |
| return "Data Network Registration Failure"; |
| case RADIO_NOT_AVAILABLE: |
| return "Radio Not Available"; |
| default: |
| return "Unknown Data Error"; |
| } |
| } |
| } |
| |
| // ***** Event codes for driving the state machine |
| protected static final int EVENT_RESET = 1; |
| protected static final int EVENT_CONNECT = 2; |
| protected static final int EVENT_SETUP_DATA_CONNECTION_DONE = 3; |
| protected static final int EVENT_GET_LAST_FAIL_DONE = 4; |
| protected static final int EVENT_DEACTIVATE_DONE = 5; |
| protected static final int EVENT_DISCONNECT = 6; |
| |
| //***** Tag IDs for EventLog |
| protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100; |
| |
| //***** Member Variables |
| protected int mTag; |
| protected PhoneBase phone; |
| protected int cid; |
| protected LinkProperties mLinkProperties = new LinkProperties(); |
| protected LinkCapabilities mCapabilities = new LinkCapabilities(); |
| protected long createTime; |
| protected long lastFailTime; |
| protected FailCause lastFailCause; |
| protected static final String NULL_IP = "0.0.0.0"; |
| Object userData; |
| |
| //***** Abstract methods |
| public abstract String toString(); |
| |
| protected abstract void onConnect(ConnectionParams cp); |
| |
| protected abstract FailCause getFailCauseFromRequest(int rilCause); |
| |
| protected abstract boolean isDnsOk(String[] domainNameServers); |
| |
| protected abstract void log(String s); |
| |
| |
| //***** Constructor |
| protected DataConnection(PhoneBase phone, String name) { |
| super(name); |
| if (DBG) log("DataConnection constructor E"); |
| this.phone = phone; |
| this.cid = -1; |
| clearSettings(); |
| |
| setDbg(false); |
| addState(mDefaultState); |
| addState(mInactiveState, mDefaultState); |
| addState(mActivatingState, mDefaultState); |
| addState(mActiveState, mDefaultState); |
| addState(mDisconnectingState, mDefaultState); |
| addState(mDisconnectingBadDnsState, mDefaultState); |
| setInitialState(mInactiveState); |
| if (DBG) log("DataConnection constructor X"); |
| } |
| |
| /** |
| * TearDown the data connection. |
| * |
| * @param o will be returned in AsyncResult.userObj |
| * and is either a DisconnectParams or ConnectionParams. |
| */ |
| private void tearDownData(Object o) { |
| if (phone.mCM.getRadioState().isOn()) { |
| if (DBG) log("tearDownData radio is on, call deactivateDataCall"); |
| phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_DEACTIVATE_DONE, 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, ar)); |
| } |
| } |
| |
| /** |
| * Send the connectionCompletedMsg. |
| * |
| * @param cp is the ConnectionParams |
| * @param cause |
| */ |
| private void notifyConnectCompleted(ConnectionParams cp, FailCause cause) { |
| Message connectionCompletedMsg = cp.onCompletedMsg; |
| if (connectionCompletedMsg == null) { |
| return; |
| } |
| |
| long timeStamp = System.currentTimeMillis(); |
| connectionCompletedMsg.arg1 = cid; |
| |
| if (cause == FailCause.NONE) { |
| createTime = timeStamp; |
| AsyncResult.forMessage(connectionCompletedMsg); |
| } else { |
| lastFailCause = cause; |
| lastFailTime = timeStamp; |
| AsyncResult.forMessage(connectionCompletedMsg, cause, new Exception()); |
| } |
| if (DBG) log("notifyConnection at " + timeStamp + " cause=" + cause); |
| |
| connectionCompletedMsg.sendToTarget(); |
| } |
| |
| /** |
| * Send ar.userObj if its a message, which is should be back to originator. |
| * |
| * @param dp is the DisconnectParams. |
| */ |
| private void notifyDisconnectCompleted(DisconnectParams dp) { |
| if (DBG) log("NotifyDisconnectCompleted"); |
| |
| if (dp.onCompletedMsg != null) { |
| Message msg = dp.onCompletedMsg; |
| log(String.format("msg.what=%d msg.obj=%s", |
| msg.what, ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>"))); |
| AsyncResult.forMessage(msg); |
| msg.sendToTarget(); |
| } |
| if (dp.lockObj != null) { |
| synchronized(dp.lockObj) { |
| dp.lockObj.notify(); |
| } |
| } |
| |
| clearSettings(); |
| } |
| |
| /** |
| * Clear all settings called when entering mInactiveState. |
| */ |
| protected void clearSettings() { |
| if (DBG) log("clearSettings"); |
| |
| this.createTime = -1; |
| this.lastFailTime = -1; |
| this.lastFailCause = FailCause.NONE; |
| |
| mLinkProperties = new LinkProperties(); |
| } |
| |
| /** |
| * Process setup completion. |
| * |
| * @param ar is the result |
| * @return SetupResult. |
| */ |
| private SetupResult onSetupConnectionCompleted(AsyncResult ar) { |
| SetupResult result; |
| String[] response = ((String[]) ar.result); |
| ConnectionParams cp = (ConnectionParams) ar.userObj; |
| |
| if (ar.exception != null) { |
| if (DBG) log("DataConnection Init failed " + ar.exception); |
| |
| if (ar.exception instanceof CommandException |
| && ((CommandException) (ar.exception)).getCommandError() |
| == CommandException.Error.RADIO_NOT_AVAILABLE) { |
| result = SetupResult.ERR_BadCommand; |
| result.mFailCause = FailCause.RADIO_NOT_AVAILABLE; |
| } else { |
| result = SetupResult.ERR_Other; |
| } |
| } else if (cp.tag != mTag) { |
| if (DBG) { |
| log("BUG: onSetupConnectionCompleted is stale cp.tag=" + cp.tag + ", mtag=" + mTag); |
| } |
| result = SetupResult.ERR_Stale; |
| } else { |
| // log("onSetupConnectionCompleted received " + response.length + " response strings:"); |
| // for (int i = 0; i < response.length; i++) { |
| // log(" response[" + i + "]='" + response[i] + "'"); |
| // } |
| |
| // Start with clean network properties and if we have |
| // a failure we'll clear again at the bottom of this code. |
| LinkProperties linkProperties = new LinkProperties(); |
| if (response.length >= 2) { |
| cid = Integer.parseInt(response[0]); |
| String interfaceName = response[1]; |
| result = SetupResult.SUCCESS; |
| |
| try { |
| String prefix = "net." + interfaceName + "."; |
| |
| NetworkInterface networkInterface = NetworkInterface.getByName(interfaceName); |
| linkProperties.setInterfaceName(interfaceName); |
| |
| // TODO: Get gateway and dns via RIL interface not property? |
| String gatewayAddress = SystemProperties.get(prefix + "gw"); |
| linkProperties.setGateway(InetAddress.getByName(gatewayAddress)); |
| |
| for (InterfaceAddress addr : networkInterface.getInterfaceAddresses()) { |
| linkProperties.addLinkAddress(new LinkAddress(addr)); |
| } |
| // TODO: Get gateway and dns via RIL interface not property? |
| String dnsServers[] = new String[2]; |
| dnsServers[0] = SystemProperties.get(prefix + "dns1"); |
| dnsServers[1] = SystemProperties.get(prefix + "dns2"); |
| if (isDnsOk(dnsServers)) { |
| linkProperties.addDns(InetAddress.getByName(dnsServers[0])); |
| linkProperties.addDns(InetAddress.getByName(dnsServers[1])); |
| } else { |
| result = SetupResult.ERR_BadDns; |
| } |
| } catch (UnknownHostException e1) { |
| log("onSetupCompleted: UnknowHostException " + e1); |
| e1.printStackTrace(); |
| result = SetupResult.ERR_Other; |
| } catch (SocketException e2) { |
| log("onSetupCompleted: SocketException " + e2); |
| e2.printStackTrace(); |
| result = SetupResult.ERR_Other; |
| } |
| } else { |
| log("onSetupCompleted: error; expected number of responses >= 2 was " + |
| response.length); |
| result = SetupResult.ERR_Other; |
| } |
| |
| // An error occurred so clear properties |
| if (result != SetupResult.SUCCESS) { |
| log("onSetupCompleted with an error clearing LinkProperties"); |
| linkProperties.clear(); |
| } |
| mLinkProperties = linkProperties; |
| } |
| |
| if (DBG) { |
| log("DataConnection setup result='" + result + "' on cid=" + cid); |
| if (result == SetupResult.SUCCESS) { |
| log("LinkProperties: " + mLinkProperties.toString()); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * The parent state for all other states. |
| */ |
| private class DcDefaultState extends HierarchicalState { |
| @Override |
| protected boolean processMessage(Message msg) { |
| AsyncResult ar; |
| |
| switch (msg.what) { |
| case EVENT_RESET: |
| if (DBG) log("DcDefaultState: msg.what=EVENT_RESET"); |
| clearSettings(); |
| if (msg.obj != null) { |
| notifyDisconnectCompleted((DisconnectParams) msg.obj); |
| } |
| transitionTo(mInactiveState); |
| break; |
| |
| case EVENT_CONNECT: |
| if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected"); |
| ConnectionParams cp = (ConnectionParams) msg.obj; |
| notifyConnectCompleted(cp, FailCause.UNKNOWN); |
| break; |
| |
| case EVENT_DISCONNECT: |
| if (DBG) log("DcDefaultState: msg.what=EVENT_DISCONNECT"); |
| notifyDisconnectCompleted((DisconnectParams) msg.obj); |
| break; |
| |
| default: |
| if (DBG) { |
| log("DcDefaultState: shouldn't happen but ignore msg.what=" + msg.what); |
| } |
| break; |
| } |
| |
| return true; |
| } |
| } |
| private DcDefaultState mDefaultState = new DcDefaultState(); |
| |
| /** |
| * The state machine is inactive and expects a EVENT_CONNECT. |
| */ |
| private class DcInactiveState extends HierarchicalState { |
| private ConnectionParams mConnectionParams = null; |
| private FailCause mFailCause = null; |
| private DisconnectParams mDisconnectParams = null; |
| |
| public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) { |
| log("DcInactiveState: setEnterNoticationParams cp,cause"); |
| mConnectionParams = cp; |
| mFailCause = cause; |
| } |
| |
| public void setEnterNotificationParams(DisconnectParams dp) { |
| log("DcInactiveState: setEnterNoticationParams dp"); |
| mDisconnectParams = dp; |
| } |
| |
| @Override protected void enter() { |
| mTag += 1; |
| |
| /** |
| * Now that we've transitioned to Inactive state we |
| * can send notifications. Previously we sent the |
| * notifications in the processMessage handler but |
| * that caused a race condition because the synchronous |
| * call to isInactive. |
| */ |
| if ((mConnectionParams != null) && (mFailCause != null)) { |
| log("DcInactiveState: enter notifyConnectCompleted"); |
| notifyConnectCompleted(mConnectionParams, mFailCause); |
| } |
| if (mDisconnectParams != null) { |
| log("DcInactiveState: enter notifyDisconnectCompleted"); |
| notifyDisconnectCompleted(mDisconnectParams); |
| } |
| } |
| |
| @Override protected void exit() { |
| // clear notifications |
| mConnectionParams = null; |
| mFailCause = null; |
| mDisconnectParams = null; |
| } |
| |
| @Override protected boolean processMessage(Message msg) { |
| boolean retVal; |
| |
| switch (msg.what) { |
| case EVENT_RESET: |
| if (DBG) { |
| log("DcInactiveState: msg.what=EVENT_RESET, ignore we're already reset"); |
| } |
| if (msg.obj != null) { |
| notifyDisconnectCompleted((DisconnectParams) msg.obj); |
| } |
| retVal = true; |
| break; |
| |
| case EVENT_CONNECT: |
| if (DBG) log("DcInactiveState msg.what=EVENT_CONNECT"); |
| ConnectionParams cp = (ConnectionParams) msg.obj; |
| cp.tag = mTag; |
| onConnect(cp); |
| transitionTo(mActivatingState); |
| retVal = true; |
| break; |
| |
| default: |
| if (DBG) log("DcInactiveState nothandled msg.what=" + msg.what); |
| retVal = false; |
| break; |
| } |
| return retVal; |
| } |
| } |
| private DcInactiveState mInactiveState = new DcInactiveState(); |
| |
| /** |
| * The state machine is activating a connection. |
| */ |
| private class DcActivatingState extends HierarchicalState { |
| @Override protected boolean processMessage(Message msg) { |
| boolean retVal; |
| AsyncResult ar; |
| ConnectionParams cp; |
| |
| switch (msg.what) { |
| case EVENT_DISCONNECT: |
| if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT"); |
| deferMessage(msg); |
| retVal = true; |
| break; |
| |
| case EVENT_SETUP_DATA_CONNECTION_DONE: |
| if (DBG) log("DcActivatingState msg.what=EVENT_SETUP_DATA_CONNECTION_DONE"); |
| |
| ar = (AsyncResult) msg.obj; |
| cp = (ConnectionParams) ar.userObj; |
| |
| SetupResult result = onSetupConnectionCompleted(ar); |
| switch (result) { |
| case SUCCESS: |
| // All is well |
| mActiveState.setEnterNotificationParams(cp, FailCause.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_BadDns: |
| // Connection succeeded but DNS info is bad so disconnect |
| StringBuilder dnsAddressesSb = new StringBuilder(); |
| for (InetAddress addr : mLinkProperties.getDnses()) { |
| if (dnsAddressesSb.length() != 0) dnsAddressesSb.append(" "); |
| dnsAddressesSb.append(addr.toString()); |
| } |
| if (dnsAddressesSb.length() == 0) { |
| dnsAddressesSb.append("no-dns-addresses"); |
| } |
| EventLog.writeEvent(EventLogTags.PDP_BAD_DNS_ADDRESS, |
| dnsAddressesSb.toString()); |
| tearDownData(cp); |
| transitionTo(mDisconnectingBadDnsState); |
| break; |
| case ERR_Other: |
| // Request the failure cause and process in this state |
| phone.mCM.getLastDataCallFailCause( |
| obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp)); |
| break; |
| case ERR_Stale: |
| // Request is stale, ignore. |
| break; |
| default: |
| throw new RuntimeException("Unkown SetupResult, should not happen"); |
| } |
| retVal = true; |
| break; |
| |
| case EVENT_GET_LAST_FAIL_DONE: |
| ar = (AsyncResult) msg.obj; |
| cp = (ConnectionParams) ar.userObj; |
| FailCause cause = FailCause.UNKNOWN; |
| |
| if (cp.tag == mTag) { |
| if (DBG) log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"); |
| if (ar.exception == null) { |
| int rilFailCause = ((int[]) (ar.result))[0]; |
| cause = getFailCauseFromRequest(rilFailCause); |
| } |
| // Transition to inactive but send notifications after |
| // we've entered the mInactive state. |
| mInactiveState.setEnterNotificationParams(cp, cause); |
| transitionTo(mInactiveState); |
| } else { |
| if (DBG) { |
| log("DcActivatingState EVENT_GET_LAST_FAIL_DONE is stale cp.tag=" |
| + cp.tag + ", mTag=" + mTag); |
| } |
| } |
| |
| retVal = true; |
| break; |
| |
| default: |
| if (DBG) log("DcActivatingState not handled msg.what=" + msg.what); |
| retVal = false; |
| break; |
| } |
| return retVal; |
| } |
| } |
| private DcActivatingState mActivatingState = new DcActivatingState(); |
| |
| /** |
| * The state machine is connected, expecting an EVENT_DISCONNECT. |
| */ |
| private class DcActiveState extends HierarchicalState { |
| private ConnectionParams mConnectionParams = null; |
| private FailCause mFailCause = null; |
| |
| public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) { |
| log("DcInactiveState: setEnterNoticationParams cp,cause"); |
| mConnectionParams = cp; |
| mFailCause = cause; |
| } |
| |
| @Override public void enter() { |
| /** |
| * Now that we've transitioned to Active state we |
| * can send notifications. Previously we sent the |
| * notifications in the processMessage handler but |
| * that caused a race condition because the synchronous |
| * call to isActive. |
| */ |
| if ((mConnectionParams != null) && (mFailCause != null)) { |
| log("DcActiveState: enter notifyConnectCompleted"); |
| notifyConnectCompleted(mConnectionParams, mFailCause); |
| } |
| } |
| |
| @Override protected void exit() { |
| // clear notifications |
| mConnectionParams = null; |
| mFailCause = null; |
| } |
| |
| @Override protected boolean processMessage(Message msg) { |
| boolean retVal; |
| |
| switch (msg.what) { |
| case EVENT_DISCONNECT: |
| if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT"); |
| DisconnectParams dp = (DisconnectParams) msg.obj; |
| dp.tag = mTag; |
| tearDownData(dp); |
| transitionTo(mDisconnectingState); |
| retVal = true; |
| break; |
| |
| default: |
| if (DBG) log("DcActiveState nothandled msg.what=" + msg.what); |
| retVal = false; |
| break; |
| } |
| return retVal; |
| } |
| } |
| private DcActiveState mActiveState = new DcActiveState(); |
| |
| /** |
| * The state machine is disconnecting. |
| */ |
| private class DcDisconnectingState extends HierarchicalState { |
| @Override protected boolean processMessage(Message msg) { |
| boolean retVal; |
| |
| switch (msg.what) { |
| case EVENT_DEACTIVATE_DONE: |
| if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE"); |
| AsyncResult ar = (AsyncResult) msg.obj; |
| DisconnectParams dp = (DisconnectParams) ar.userObj; |
| if (dp.tag == 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 EVENT_DEACTIVATE_DONE stale dp.tag=" |
| + dp.tag + " mTag=" + mTag); |
| } |
| retVal = true; |
| break; |
| |
| default: |
| if (DBG) log("DcDisconnectingState not handled msg.what=" + msg.what); |
| retVal = false; |
| break; |
| } |
| return retVal; |
| } |
| } |
| private DcDisconnectingState mDisconnectingState = new DcDisconnectingState(); |
| |
| /** |
| * The state machine is disconnecting after a bad dns setup |
| * was found in mInactivatingState. |
| */ |
| private class DcDisconnectingBadDnsState extends HierarchicalState { |
| @Override protected 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.tag == mTag) { |
| if (DBG) log("DcDisconnectingBadDnsState msg.what=EVENT_DEACTIVATE_DONE"); |
| // Transition to inactive but send notifications after |
| // we've entered the mInactive state. |
| mInactiveState.setEnterNotificationParams(cp, FailCause.UNKNOWN); |
| transitionTo(mInactiveState); |
| } else { |
| if (DBG) log("DcDisconnectingBadDnsState EVENT_DEACTIVE_DONE stale dp.tag=" |
| + cp.tag + ", mTag=" + mTag); |
| } |
| retVal = true; |
| break; |
| |
| default: |
| if (DBG) log("DcDisconnectingBadDnsState not handled msg.what=" + msg.what); |
| retVal = false; |
| break; |
| } |
| return retVal; |
| } |
| } |
| private DcDisconnectingBadDnsState mDisconnectingBadDnsState = new DcDisconnectingBadDnsState(); |
| |
| // ******* public interface |
| |
| /** |
| * Disconnect from the network. |
| * |
| * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. |
| * With AsyncResult.userObj set to the original msg.obj. |
| */ |
| public void reset(Message onCompletedMsg) { |
| sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(onCompletedMsg))); |
| } |
| |
| /** |
| * Reset the connection and wait for it to complete. |
| * TODO: Remove when all callers only need the asynchronous |
| * reset defined above. |
| */ |
| public void resetSynchronously() { |
| ResetSynchronouslyLock lockObj = new ResetSynchronouslyLock(); |
| synchronized(lockObj) { |
| sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(lockObj))); |
| try { |
| lockObj.wait(); |
| } catch (InterruptedException e) { |
| log("blockingReset: unexpected interrupted of wait()"); |
| } |
| } |
| } |
| |
| /** |
| * Connect to the apn and return an AsyncResult in onCompletedMsg. |
| * Used for cellular networks that use Acess Point Names (APN) such |
| * as GSM networks. |
| * |
| * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. |
| * With AsyncResult.userObj set to the original msg.obj, |
| * AsyncResult.result = FailCause and AsyncResult.exception = Exception(). |
| * @param apn is the Acces Point Name to connect to |
| */ |
| public void connect(Message onCompletedMsg, ApnSetting apn) { |
| sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg))); |
| } |
| |
| /** |
| * Connect to the apn and return an AsyncResult in onCompletedMsg. |
| * |
| * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. |
| * With AsyncResult.userObj set to the original msg.obj, |
| * AsyncResult.result = FailCause and AsyncResult.exception = Exception(). |
| */ |
| public void connect(Message onCompletedMsg) { |
| sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(null, onCompletedMsg))); |
| } |
| |
| /** |
| * Disconnect from the network. |
| * |
| * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. |
| * With AsyncResult.userObj set to the original msg.obj. |
| */ |
| public void disconnect(Message onCompletedMsg) { |
| sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(onCompletedMsg))); |
| } |
| |
| // ****** The following are used for debugging. |
| |
| /** |
| * TODO: This should be an asynchronous call and we wouldn't |
| * have to use handle the notification in the DcInactiveState.enter. |
| * |
| * @return true if the state machine is in the inactive state. |
| */ |
| public boolean isInactive() { |
| boolean retVal = getCurrentState() == mInactiveState; |
| return retVal; |
| } |
| |
| /** |
| * TODO: This should be an asynchronous call and we wouldn't |
| * have to use handle the notification in the DcActiveState.enter. |
| * |
| * @return true if the state machine is in the active state. |
| */ |
| public boolean isActive() { |
| boolean retVal = getCurrentState() == mActiveState; |
| return retVal; |
| } |
| |
| /** |
| * Return the LinkProperties for the connection. |
| * |
| * @return a copy of the LinkProperties, is never null. |
| */ |
| public LinkProperties getLinkProperties() { |
| return new LinkProperties(mLinkProperties); |
| } |
| |
| /** |
| * A capability is an Integer/String pair, the capabilities |
| * are defined in the class LinkSocket#Key. |
| * |
| * @return a copy of this connections capabilities, may be empty but never null. |
| */ |
| public LinkCapabilities getLinkCapabilities() { |
| return new LinkCapabilities(mCapabilities); |
| } |
| |
| /** |
| * @return the current state as a string. |
| */ |
| public String getStateAsString() { |
| String retVal = getCurrentState().getName(); |
| return retVal; |
| } |
| |
| /** |
| * @return the time of when this connection was created. |
| */ |
| public long getConnectionTime() { |
| return createTime; |
| } |
| |
| /** |
| * @return the time of the last failure. |
| */ |
| public long getLastFailTime() { |
| return lastFailTime; |
| } |
| |
| /** |
| * @return the last cause of failure. |
| */ |
| public FailCause getLastFailCause() { |
| return lastFailCause; |
| } |
| } |