If a DataConnection is pending re-connect alarm, the new request from another ApnContext sharing the same DC could disrupt the re-connection pattern currently engaged. This patch is to ensure the new request for PDP sharing scenario will not trigger another SETUP_DATA request if reconnection alarm is set in AlarmManager. Bug: 4901019 Change-Id: I98b0d9af8b58fb880efdbc0246009de5cb116a54
1138 lines
44 KiB
Java
1138 lines
44 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.app.PendingIntent;
|
|
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.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
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 final boolean VDBG = false;
|
|
|
|
protected static Object mCountLock = new Object();
|
|
protected static int mCount;
|
|
protected AsyncChannel mAc;
|
|
|
|
private List<ApnContext> mApnList = null;
|
|
PendingIntent mReconnectIntent = null;
|
|
|
|
/**
|
|
* 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.getErrorCode(), 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;
|
|
}
|
|
}
|
|
|
|
public static class CallSetupException extends Exception {
|
|
private int mRetryOverride = -1;
|
|
|
|
CallSetupException (int retryOverride) {
|
|
mRetryOverride = retryOverride;
|
|
}
|
|
|
|
public int getRetryOverride() {
|
|
return mRetryOverride;
|
|
}
|
|
}
|
|
|
|
// ***** 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 int mRetryOverride = -1;
|
|
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;
|
|
|
|
setDbg(false);
|
|
addState(mDefaultState);
|
|
addState(mInactiveState, mDefaultState);
|
|
addState(mActivatingState, mDefaultState);
|
|
addState(mActiveState, mDefaultState);
|
|
addState(mDisconnectingState, mDefaultState);
|
|
addState(mDisconnectingErrorCreatingConnection, mDefaultState);
|
|
setInitialState(mInactiveState);
|
|
|
|
mApnList = new ArrayList<ApnContext>();
|
|
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 CallSetupException(mRetryOverride));
|
|
}
|
|
if (DBG) log("notifyConnectionCompleted 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 (VDBG) log("NotifyDisconnectCompleted");
|
|
|
|
if (dp.onCompletedMsg != null) {
|
|
Message msg = dp.onCompletedMsg;
|
|
if (VDBG) {
|
|
log(String.format("msg=%s msg.obj=%s", msg.toString(),
|
|
((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
|
|
}
|
|
AsyncResult.forMessage(msg);
|
|
msg.sendToTarget();
|
|
}
|
|
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;
|
|
mRetryOverride = -1;
|
|
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 {
|
|
if (DBG) log("onSetupConnectionCompleted received DataCallState: " + response);
|
|
cid = response.cid;
|
|
// set link properties based on data call response
|
|
result = setLinkProperties(response, mLinkProperties);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private int getSuggestedRetryTime(AsyncResult ar) {
|
|
int retry = -1;
|
|
if (ar.exception == null) {
|
|
DataCallState response = (DataCallState) ar.result;
|
|
retry = response.suggestedRetryTime;
|
|
}
|
|
return retry;
|
|
}
|
|
|
|
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) {
|
|
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_DISCONNECT: {
|
|
if (VDBG) log("CMD_CHANNEL_DISCONNECT");
|
|
mAc.disconnect();
|
|
break;
|
|
}
|
|
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
|
|
if (VDBG) log("CMD_CHANNEL_DISCONNECTED");
|
|
mAc = null;
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_IS_INACTIVE: {
|
|
boolean val = getCurrentState() == mInactiveState;
|
|
if (VDBG) log("REQ_IS_INACTIVE isInactive=" + val);
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_IS_INACTIVE, val ? 1 : 0);
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_GET_CID: {
|
|
if (VDBG) log("REQ_GET_CID cid=" + cid);
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_CID, cid);
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_GET_APNSETTING: {
|
|
if (VDBG) 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);
|
|
if (VDBG) 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;
|
|
if (VDBG) 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 (VDBG) {
|
|
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);
|
|
if (VDBG) log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc);
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_CAPABILITIES, lc);
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_RESET:
|
|
if (VDBG) log("DcDefaultState: msg.what=REQ_RESET");
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
|
|
transitionTo(mInactiveState);
|
|
break;
|
|
case DataConnectionAc.REQ_GET_REFCOUNT: {
|
|
if (VDBG) log("REQ_GET_REFCOUNT refCount=" + mRefCount);
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_REFCOUNT, mRefCount);
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_ADD_APNCONTEXT: {
|
|
ApnContext apnContext = (ApnContext) msg.obj;
|
|
if (VDBG) log("REQ_ADD_APNCONTEXT apn=" + apnContext.getApnType());
|
|
if (!mApnList.contains(apnContext)) {
|
|
mApnList.add(apnContext);
|
|
}
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_ADD_APNCONTEXT);
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_REMOVE_APNCONTEXT: {
|
|
ApnContext apnContext = (ApnContext) msg.obj;
|
|
if (VDBG) log("REQ_REMOVE_APNCONTEXT apn=" + apnContext.getApnType());
|
|
mApnList.remove(apnContext);
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_REMOVE_APNCONTEXT);
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_GET_APNCONTEXT_LIST: {
|
|
if (VDBG) log("REQ_GET_APNCONTEXT_LIST num in list=" + mApnList.size());
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNCONTEXT_LIST,
|
|
new ArrayList(mApnList));
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_SET_RECONNECT_INTENT: {
|
|
PendingIntent intent = (PendingIntent) msg.obj;
|
|
if (VDBG) log("REQ_SET_RECONNECT_INTENT");
|
|
mReconnectIntent = intent;
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_RECONNECT_INTENT);
|
|
break;
|
|
}
|
|
case DataConnectionAc.REQ_GET_RECONNECT_INTENT: {
|
|
if (VDBG) log("REQ_GET_RECONNECT_INTENT");
|
|
mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_RECONNECT_INTENT,
|
|
mReconnectIntent);
|
|
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=0x" +
|
|
Integer.toHexString(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,
|
|
int retryOverride) {
|
|
if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause");
|
|
mConnectionParams = cp;
|
|
mFailCause = cause;
|
|
mRetryOverride = retryOverride;
|
|
}
|
|
|
|
public void setEnterNotificationParams(DisconnectParams dp) {
|
|
if (VDBG) 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)) {
|
|
if (VDBG) log("DcInactiveState: enter notifyConnectCompleted");
|
|
notifyConnectCompleted(mConnectionParams, mFailCause);
|
|
}
|
|
if (mDisconnectParams != null) {
|
|
if (VDBG) log("DcInactiveState: enter notifyDisconnectCompleted");
|
|
notifyDisconnectCompleted(mDisconnectParams);
|
|
}
|
|
clearSettings();
|
|
}
|
|
|
|
@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 (VDBG) {
|
|
log("DcInactiveState nothandled msg.what=0x" +
|
|
Integer.toHexString(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, -1);
|
|
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,
|
|
getSuggestedRetryTime(ar));
|
|
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, -1);
|
|
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 (VDBG) {
|
|
log("DcActivatingState not handled msg.what=0x" +
|
|
Integer.toHexString(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) {
|
|
if (VDBG) 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)) {
|
|
if (VDBG) 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 (VDBG) {
|
|
log("DcActiveState not handled msg.what=0x" +
|
|
Integer.toHexString(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 (VDBG) {
|
|
log("DcDisconnectingState not handled msg.what=0x" +
|
|
Integer.toHexString(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, -1);
|
|
transitionTo(mInactiveState);
|
|
} else {
|
|
if (DBG) {
|
|
log("DcDisconnectionErrorCreatingConnection EVENT_DEACTIVATE_DONE" +
|
|
" stale dp.tag=" + cp.tag + ", mTag=" + mTag);
|
|
}
|
|
}
|
|
retVal = HANDLED;
|
|
break;
|
|
|
|
default:
|
|
if (VDBG) {
|
|
log("DcDisconnectionErrorCreatingConnection not handled msg.what=0x"
|
|
+ Integer.toHexString(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)));
|
|
}
|
|
}
|