With current design the code maps to the same DC, but no indications go out. With this, making it more async in nature that the request goes all the way to DC and all the data indications would be triggered by parallel paths through DCT. Change-Id: I4c6e64912dafe19154d910bbd0441b10ada36cff
1056 lines
40 KiB
Java
1056 lines
40 KiB
Java
/*
|
|
* Copyright (C) 2006 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.internal.telephony;
|
|
|
|
|
|
import 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.net.LinkAddress;
|
|
import android.net.LinkCapabilities;
|
|
import android.net.LinkProperties;
|
|
import android.net.NetworkUtils;
|
|
import android.net.ProxyProperties;
|
|
import android.os.AsyncResult;
|
|
import android.os.Bundle;
|
|
import android.os.Message;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
import android.os.SystemProperties;
|
|
import android.text.TextUtils;
|
|
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
/**
|
|
* {@hide}
|
|
*
|
|
* DataConnection StateMachine.
|
|
*
|
|
* 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.
|
|
*/
|
|
public abstract class DataConnection extends StateMachine {
|
|
protected static final boolean DBG = true;
|
|
|
|
protected static Object mCountLock = new Object();
|
|
protected static int mCount;
|
|
protected AsyncChannel mAc;
|
|
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* Used internally for saving disconnecting parameters.
|
|
*/
|
|
protected static class DisconnectParams {
|
|
public DisconnectParams(String reason, Message onCompletedMsg) {
|
|
this.reason = reason;
|
|
this.onCompletedMsg = onCompletedMsg;
|
|
}
|
|
public int tag;
|
|
public String reason;
|
|
public Message onCompletedMsg;
|
|
}
|
|
|
|
/**
|
|
* Returned as the reason for a connection failure as defined
|
|
* by RIL_DataCallFailCause in ril.h and some local errors.
|
|
*/
|
|
public enum FailCause {
|
|
NONE(0),
|
|
|
|
// This series of errors as specified by the standards
|
|
// specified in ril.h
|
|
OPERATOR_BARRED(0x08),
|
|
INSUFFICIENT_RESOURCES(0x1A),
|
|
MISSING_UNKNOWN_APN(0x1B),
|
|
UNKNOWN_PDP_ADDRESS_TYPE(0x1C),
|
|
USER_AUTHENTICATION(0x1D),
|
|
ACTIVATION_REJECT_GGSN(0x1E),
|
|
ACTIVATION_REJECT_UNSPECIFIED(0x1F),
|
|
SERVICE_OPTION_NOT_SUPPORTED(0x20),
|
|
SERVICE_OPTION_NOT_SUBSCRIBED(0x21),
|
|
SERVICE_OPTION_OUT_OF_ORDER(0x22),
|
|
NSAPI_IN_USE(0x23),
|
|
ONLY_IPV4_ALLOWED(0x32),
|
|
ONLY_IPV6_ALLOWED(0x33),
|
|
ONLY_SINGLE_BEARER_ALLOWED(0x34),
|
|
PROTOCOL_ERRORS(0x6F),
|
|
|
|
// Local errors generated by Vendor RIL
|
|
// specified in ril.h
|
|
REGISTRATION_FAIL(-1),
|
|
GPRS_REGISTRATION_FAIL(-2),
|
|
SIGNAL_LOST(-3),
|
|
PREF_RADIO_TECH_CHANGED(-4),
|
|
RADIO_POWER_OFF(-5),
|
|
TETHERED_CALL_ACTIVE(-6),
|
|
ERROR_UNSPECIFIED(0xFFFF),
|
|
|
|
// Errors generated by the Framework
|
|
// specified here
|
|
UNKNOWN(0x10000),
|
|
RADIO_NOT_AVAILABLE(0x10001),
|
|
UNACCEPTABLE_NETWORK_PARAMETER(0x10002);
|
|
|
|
private final int mErrorCode;
|
|
private static final HashMap<Integer, FailCause> sErrorCodeToFailCauseMap;
|
|
static {
|
|
sErrorCodeToFailCauseMap = new HashMap<Integer, FailCause>();
|
|
for (FailCause fc : values()) {
|
|
sErrorCodeToFailCauseMap.put(fc.ordinal(), fc);
|
|
}
|
|
}
|
|
|
|
FailCause(int errorCode) {
|
|
mErrorCode = errorCode;
|
|
}
|
|
|
|
int getErrorCode() {
|
|
return mErrorCode;
|
|
}
|
|
|
|
public boolean isPermanentFail() {
|
|
return (this == OPERATOR_BARRED) || (this == MISSING_UNKNOWN_APN) ||
|
|
(this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
|
|
(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_TYPE) || (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) ||
|
|
(this == UNACCEPTABLE_NETWORK_PARAMETER);
|
|
}
|
|
|
|
public static FailCause fromInt(int errorCode) {
|
|
FailCause fc = sErrorCodeToFailCauseMap.get(errorCode);
|
|
if (fc == null) {
|
|
fc = UNKNOWN;
|
|
}
|
|
return fc;
|
|
}
|
|
}
|
|
|
|
// ***** Event codes for driving the state machine
|
|
protected static final int BASE = Protocol.BASE_DATA_CONNECTION;
|
|
protected static final int EVENT_CONNECT = BASE + 0;
|
|
protected static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1;
|
|
protected static final int EVENT_GET_LAST_FAIL_DONE = BASE + 2;
|
|
protected static final int EVENT_DEACTIVATE_DONE = BASE + 3;
|
|
protected static final int EVENT_DISCONNECT = BASE + 4;
|
|
protected static final int EVENT_RIL_CONNECTED = BASE + 5;
|
|
|
|
//***** Tag IDs for EventLog
|
|
protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100;
|
|
|
|
//***** Member Variables
|
|
protected ApnSetting mApn;
|
|
protected int mTag;
|
|
protected PhoneBase phone;
|
|
protected int mRilVersion = -1;
|
|
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";
|
|
private int mRefCount;
|
|
Object userData;
|
|
|
|
//***** Abstract methods
|
|
@Override
|
|
public abstract String toString();
|
|
|
|
protected abstract void onConnect(ConnectionParams cp);
|
|
|
|
protected abstract boolean isDnsOk(String[] domainNameServers);
|
|
|
|
protected abstract void log(String s);
|
|
|
|
|
|
//***** Constructor
|
|
protected DataConnection(PhoneBase phone, String name, int id, RetryManager rm) {
|
|
super(name);
|
|
if (DBG) log("DataConnection constructor E");
|
|
this.phone = phone;
|
|
mId = id;
|
|
mRetryMgr = rm;
|
|
this.cid = -1;
|
|
clearSettings();
|
|
|
|
setDbg(false);
|
|
addState(mDefaultState);
|
|
addState(mInactiveState, mDefaultState);
|
|
addState(mActivatingState, mDefaultState);
|
|
addState(mActiveState, mDefaultState);
|
|
addState(mDisconnectingState, mDefaultState);
|
|
addState(mDisconnectingErrorCreatingConnection, 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) {
|
|
int discReason = RILConstants.DEACTIVATE_REASON_NONE;
|
|
if ((o != null) && (o instanceof DisconnectParams)) {
|
|
DisconnectParams dp = (DisconnectParams)o;
|
|
Message m = dp.onCompletedMsg;
|
|
if (TextUtils.equals(dp.reason, Phone.REASON_RADIO_TURNED_OFF)) {
|
|
discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
|
|
} else if (TextUtils.equals(dp.reason, Phone.REASON_PDP_RESET)) {
|
|
discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
|
|
}
|
|
}
|
|
if (phone.mCM.getRadioState().isOn()) {
|
|
if (DBG) log("tearDownData radio is on, call deactivateDataCall");
|
|
phone.mCM.deactivateDataCall(cid, discReason, 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=%s msg.obj=%s", msg.toString(),
|
|
((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
|
|
AsyncResult.forMessage(msg);
|
|
msg.sendToTarget();
|
|
}
|
|
clearSettings();
|
|
if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp);
|
|
}
|
|
|
|
protected int getRadioTechnology(int defaultRadioTechnology) {
|
|
int radioTechnology;
|
|
if (mRilVersion < 6) {
|
|
radioTechnology = defaultRadioTechnology;
|
|
} else {
|
|
radioTechnology = phone.getServiceState().getRadioTechnology() + 2;
|
|
}
|
|
return radioTechnology;
|
|
}
|
|
|
|
/*
|
|
* **************************************************************************
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* The retry manager is currently owned by the DataConnectionTracker but is stored
|
|
* in the DataConnection because there is one per connection. These methods
|
|
* should only be used by the DataConnectionTracker although someday the retrying
|
|
* maybe managed by the DataConnection itself and these methods could disappear.
|
|
*/
|
|
private RetryManager mRetryMgr;
|
|
|
|
/**
|
|
* @return retry manager retryCount
|
|
*/
|
|
public int getRetryCount() {
|
|
return mRetryMgr.getRetryCount();
|
|
}
|
|
|
|
/**
|
|
* @return retry manager retryTimer
|
|
*/
|
|
public int getRetryTimer() {
|
|
return mRetryMgr.getRetryTimer();
|
|
}
|
|
|
|
/**
|
|
* increaseRetryCount of retry manager
|
|
*/
|
|
public void increaseRetryCount() {
|
|
mRetryMgr.increaseRetryCount();
|
|
}
|
|
|
|
/**
|
|
* @return retry manager isRetryNeeded
|
|
*/
|
|
public boolean isRetryNeeded() {
|
|
return mRetryMgr.isRetryNeeded();
|
|
}
|
|
|
|
/**
|
|
* resetRetryCount of retry manager
|
|
*/
|
|
public void resetRetryCount() {
|
|
mRetryMgr.resetRetryCount();
|
|
}
|
|
|
|
/**
|
|
* set retryForeverUsingLasttimeout of retry manager
|
|
*/
|
|
public void retryForeverUsingLastTimeout() {
|
|
mRetryMgr.retryForeverUsingLastTimeout();
|
|
}
|
|
|
|
/**
|
|
* @return retry manager isRetryForever
|
|
*/
|
|
public boolean isRetryForever() {
|
|
return mRetryMgr.isRetryForever();
|
|
}
|
|
|
|
/**
|
|
* @return whether the retry config is set successfully or not
|
|
*/
|
|
public boolean configureRetry(int maxRetryCount, int retryTime, int randomizationTime) {
|
|
return mRetryMgr.configure(maxRetryCount, retryTime, randomizationTime);
|
|
}
|
|
|
|
/**
|
|
* @return whether the retry config is set successfully or not
|
|
*/
|
|
public boolean configureRetry(String configStr) {
|
|
return mRetryMgr.configure(configStr);
|
|
}
|
|
|
|
/*
|
|
* **************************************************************************
|
|
* End members owned by DataConnectionTracker
|
|
* **************************************************************************
|
|
*/
|
|
|
|
/**
|
|
* Clear all settings called when entering mInactiveState.
|
|
*/
|
|
protected void clearSettings() {
|
|
if (DBG) log("clearSettings");
|
|
|
|
createTime = -1;
|
|
lastFailTime = -1;
|
|
lastFailCause = FailCause.NONE;
|
|
mRefCount = 0;
|
|
|
|
mLinkProperties = new LinkProperties();
|
|
mApn = null;
|
|
}
|
|
|
|
/**
|
|
* Process setup completion.
|
|
*
|
|
* @param ar is the result
|
|
* @return SetupResult.
|
|
*/
|
|
private DataCallState.SetupResult onSetupConnectionCompleted(AsyncResult ar) {
|
|
DataCallState response = (DataCallState) ar.result;
|
|
ConnectionParams cp = (ConnectionParams) ar.userObj;
|
|
DataCallState.SetupResult result;
|
|
|
|
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 = DataCallState.SetupResult.ERR_BadCommand;
|
|
result.mFailCause = FailCause.RADIO_NOT_AVAILABLE;
|
|
} else if ((response == null) || (response.version < 4)) {
|
|
result = DataCallState.SetupResult.ERR_GetLastErrorFromRil;
|
|
} else {
|
|
result = DataCallState.SetupResult.ERR_RilError;
|
|
result.mFailCause = FailCause.fromInt(response.status);
|
|
}
|
|
} else if (cp.tag != mTag) {
|
|
if (DBG) {
|
|
log("BUG: onSetupConnectionCompleted is stale cp.tag=" + cp.tag + ", mtag=" + mTag);
|
|
}
|
|
result = DataCallState.SetupResult.ERR_Stale;
|
|
} else if (response.status != 0) {
|
|
result = DataCallState.SetupResult.ERR_RilError;
|
|
result.mFailCause = FailCause.fromInt(response.status);
|
|
} else {
|
|
log("onSetupConnectionCompleted received DataCallState: " + response);
|
|
cid = response.cid;
|
|
// set link properties based on data call response
|
|
result = setLinkProperties(response, mLinkProperties);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private DataCallState.SetupResult setLinkProperties(DataCallState 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);
|
|
}
|
|
|
|
private DataConnectionAc.LinkPropertyChangeAction updateLinkProperty(
|
|
DataCallState newState) {
|
|
DataConnectionAc.LinkPropertyChangeAction changed =
|
|
DataConnectionAc.LinkPropertyChangeAction.NONE;
|
|
|
|
if (newState == null) return changed;
|
|
|
|
DataCallState.SetupResult result;
|
|
LinkProperties newLp = new LinkProperties();
|
|
|
|
// set link properties based on data call response
|
|
result = setLinkProperties(newState, newLp);
|
|
if (result != DataCallState.SetupResult.SUCCESS) {
|
|
if (DBG) log("UpdateLinkProperty failed : " + result);
|
|
return changed;
|
|
}
|
|
// copy HTTP proxy as it is not part DataCallState.
|
|
newLp.setHttpProxy(mLinkProperties.getHttpProxy());
|
|
|
|
if (DBG) log("old LP=" + mLinkProperties);
|
|
if (DBG) log("new LP=" + newLp);
|
|
|
|
// Check consistency of link address. Currently we expect
|
|
// only one "global" address is assigned per each IP type.
|
|
Collection<LinkAddress> oLinks = mLinkProperties.getLinkAddresses();
|
|
Collection<LinkAddress> nLinks = newLp.getLinkAddresses();
|
|
for (LinkAddress oldLink : oLinks) {
|
|
for (LinkAddress newLink : nLinks) {
|
|
if ((NetworkUtils.addressTypeMatches(oldLink.getAddress(),
|
|
newLink.getAddress())) &&
|
|
(oldLink.equals(newLink) == false)) {
|
|
return DataConnectionAc.LinkPropertyChangeAction.RESET;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mLinkProperties == null || !mLinkProperties.equals(newLp)) {
|
|
mLinkProperties = newLp;
|
|
changed = DataConnectionAc.LinkPropertyChangeAction.CHANGED;
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
/**
|
|
* The parent state for all other states.
|
|
*/
|
|
private class DcDefaultState extends State {
|
|
@Override
|
|
public void enter() {
|
|
phone.mCM.registerForRilConnected(getHandler(), EVENT_RIL_CONNECTED, null);
|
|
}
|
|
@Override
|
|
public void exit() {
|
|
phone.mCM.unregisterForRilConnected(getHandler());
|
|
}
|
|
@Override
|
|
public boolean processMessage(Message msg) {
|
|
AsyncResult ar;
|
|
|
|
switch (msg.what) {
|
|
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
|
|
if (mAc != null) {
|
|
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);
|
|
log("DcDefaultState: FULL_CONNECTION reply connected");
|
|
mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
|
|
AsyncChannel.STATUS_SUCCESSFUL, mId, "hi");
|
|
}
|
|
break;
|
|
}
|
|
case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
|
|
log("CMD_CHANNEL_DISCONNECT");
|
|
mAc.disconnect();
|
|
break;
|
|
}
|
|
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
|
|
log("CMD_CHANNEL_DISCONNECTED");
|
|
mAc = null;
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_IS_INACTIVE: {
|
|
boolean val = getCurrentState() == mInactiveState;
|
|
log("REQ_IS_INACTIVE isInactive=" + val);
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_IS_INACTIVE, val ? 1 : 0);
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_GET_CID: {
|
|
log("REQ_GET_CID cid=" + cid);
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_CID, cid);
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_GET_APNSETTING: {
|
|
log("REQ_GET_APNSETTING apnSetting=" + mApn);
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNSETTING, mApn);
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_GET_LINK_PROPERTIES: {
|
|
LinkProperties lp = new LinkProperties(mLinkProperties);
|
|
log("REQ_GET_LINK_PROPERTIES linkProperties" + lp);
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_PROPERTIES, lp);
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_SET_LINK_PROPERTIES_HTTP_PROXY: {
|
|
ProxyProperties proxy = (ProxyProperties) msg.obj;
|
|
log("REQ_SET_LINK_PROPERTIES_HTTP_PROXY proxy=" + proxy);
|
|
mLinkProperties.setHttpProxy(proxy);
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_LINK_PROPERTIES_HTTP_PROXY);
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: {
|
|
DataCallState newState = (DataCallState) msg.obj;
|
|
DataConnectionAc.LinkPropertyChangeAction action = updateLinkProperty(newState);
|
|
if (DBG) {
|
|
log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE action="
|
|
+ action + " newState=" + newState);
|
|
}
|
|
mAc.replyToMessage(msg,
|
|
DataConnectionAc.RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE,
|
|
action.ordinal());
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: {
|
|
LinkCapabilities lc = new LinkCapabilities(mCapabilities);
|
|
log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc);
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_CAPABILITIES, lc);
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_RESET:
|
|
if (DBG) log("DcDefaultState: msg.what=REQ_RESET");
|
|
clearSettings();
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
|
|
transitionTo(mInactiveState);
|
|
break;
|
|
case DataConnectionAc.REQ_GET_REFCOUNT: {
|
|
log("REQ_GET_REFCOUNT refCount=" + mRefCount);
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_REFCOUNT, mRefCount);
|
|
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;
|
|
|
|
case EVENT_RIL_CONNECTED:
|
|
ar = (AsyncResult)msg.obj;
|
|
if (ar.exception == null) {
|
|
mRilVersion = (Integer)ar.result;
|
|
if (DBG) {
|
|
log("DcDefaultState: msg.what=EVENT_RIL_CONNECTED mRilVersion=" +
|
|
mRilVersion);
|
|
}
|
|
} else {
|
|
log("Unexpected exception on EVENT_RIL_CONNECTED");
|
|
mRilVersion = -1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (DBG) {
|
|
log("DcDefaultState: shouldn't happen but ignore msg.what=" + msg.what);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return HANDLED;
|
|
}
|
|
}
|
|
private DcDefaultState mDefaultState = new DcDefaultState();
|
|
|
|
/**
|
|
* The state machine is inactive and expects a EVENT_CONNECT.
|
|
*/
|
|
private class DcInactiveState extends State {
|
|
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
|
|
public 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
|
|
public void exit() {
|
|
// clear notifications
|
|
mConnectionParams = null;
|
|
mFailCause = null;
|
|
mDisconnectParams = null;
|
|
}
|
|
|
|
@Override
|
|
public boolean processMessage(Message msg) {
|
|
boolean retVal;
|
|
|
|
switch (msg.what) {
|
|
case DataConnectionAc.REQ_RESET:
|
|
if (DBG) {
|
|
log("DcInactiveState: msg.what=RSP_RESET, ignore we're already reset");
|
|
}
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
|
|
retVal = HANDLED;
|
|
break;
|
|
|
|
case EVENT_CONNECT:
|
|
ConnectionParams cp = (ConnectionParams) msg.obj;
|
|
cp.tag = mTag;
|
|
if (DBG) {
|
|
log("DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = "
|
|
+ mRefCount);
|
|
}
|
|
mRefCount = 1;
|
|
onConnect(cp);
|
|
transitionTo(mActivatingState);
|
|
retVal = HANDLED;
|
|
break;
|
|
|
|
default:
|
|
if (DBG) log("DcInactiveState nothandled msg.what=" + msg.what);
|
|
retVal = NOT_HANDLED;
|
|
break;
|
|
}
|
|
return retVal;
|
|
}
|
|
}
|
|
private DcInactiveState mInactiveState = new DcInactiveState();
|
|
|
|
/**
|
|
* The state machine is activating a connection.
|
|
*/
|
|
private class DcActivatingState extends State {
|
|
@Override
|
|
public 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"
|
|
+ mRefCount);
|
|
deferMessage(msg);
|
|
retVal = HANDLED;
|
|
break;
|
|
|
|
case EVENT_CONNECT:
|
|
if (DBG) log("DcActivatingState deferring msg.what=EVENT_CONNECT refCount = "
|
|
+ mRefCount);
|
|
deferMessage(msg);
|
|
retVal = HANDLED;
|
|
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;
|
|
|
|
DataCallState.SetupResult result = onSetupConnectionCompleted(ar);
|
|
if (DBG) log("DcActivatingState onSetupConnectionCompleted result=" + result);
|
|
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_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
|
|
phone.mCM.getLastDataCallFailCause(
|
|
obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp));
|
|
break;
|
|
case ERR_RilError:
|
|
// Request failed and mFailCause has the reason
|
|
mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
|
|
transitionTo(mInactiveState);
|
|
break;
|
|
case ERR_Stale:
|
|
// Request is stale, ignore.
|
|
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;
|
|
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 = FailCause.fromInt(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 = HANDLED;
|
|
break;
|
|
|
|
default:
|
|
if (DBG) log("DcActivatingState not handled msg.what=" + msg.what);
|
|
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 {
|
|
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
|
|
public void exit() {
|
|
// clear notifications
|
|
mConnectionParams = null;
|
|
mFailCause = null;
|
|
}
|
|
|
|
@Override
|
|
public boolean processMessage(Message msg) {
|
|
boolean retVal;
|
|
|
|
switch (msg.what) {
|
|
case EVENT_CONNECT:
|
|
mRefCount++;
|
|
if (DBG) log("DcActiveState msg.what=EVENT_CONNECT RefCount=" + mRefCount);
|
|
if (msg.obj != null) {
|
|
notifyConnectCompleted((ConnectionParams) msg.obj, FailCause.NONE);
|
|
}
|
|
retVal = HANDLED;
|
|
break;
|
|
case EVENT_DISCONNECT:
|
|
mRefCount--;
|
|
if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT RefCount=" + mRefCount);
|
|
if (mRefCount == 0)
|
|
{
|
|
DisconnectParams dp = (DisconnectParams) msg.obj;
|
|
dp.tag = mTag;
|
|
tearDownData(dp);
|
|
transitionTo(mDisconnectingState);
|
|
} else {
|
|
if (msg.obj != null) {
|
|
notifyDisconnectCompleted((DisconnectParams) msg.obj);
|
|
}
|
|
}
|
|
retVal = HANDLED;
|
|
break;
|
|
|
|
default:
|
|
if (DBG) log("DcActiveState nothandled msg.what=" + 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 = "
|
|
+ mRefCount);
|
|
deferMessage(msg);
|
|
retVal = HANDLED;
|
|
break;
|
|
|
|
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 = HANDLED;
|
|
break;
|
|
|
|
default:
|
|
if (DBG) log("DcDisconnectingState not handled msg.what=" + 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.tag == 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,
|
|
FailCause.UNACCEPTABLE_NETWORK_PARAMETER);
|
|
transitionTo(mInactiveState);
|
|
} else {
|
|
if (DBG) {
|
|
log("DcDisconnectionErrorCreatingConnection EVENT_DEACTIVATE_DONE" +
|
|
" stale dp.tag=" + cp.tag + ", mTag=" + mTag);
|
|
}
|
|
}
|
|
retVal = HANDLED;
|
|
break;
|
|
|
|
default:
|
|
if (DBG) {
|
|
log("DcDisconnectionErrorCreatingConnection not handled msg.what="
|
|
+ msg.what);
|
|
}
|
|
retVal = NOT_HANDLED;
|
|
break;
|
|
}
|
|
return retVal;
|
|
}
|
|
}
|
|
private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection =
|
|
new DcDisconnectionErrorCreatingConnection();
|
|
|
|
// ******* public interface
|
|
|
|
/**
|
|
* Bring up a connection to the apn and return an AsyncResult in onCompletedMsg.
|
|
* Used for cellular networks that use Acesss 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 Access Point Name to bring up a connection to
|
|
*/
|
|
public void bringUp(Message onCompletedMsg, ApnSetting apn) {
|
|
sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg)));
|
|
}
|
|
|
|
/**
|
|
* Tear down the connection through the apn on 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 tearDown(String reason, Message onCompletedMsg) {
|
|
sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(reason, onCompletedMsg)));
|
|
}
|
|
}
|