Merge "Fix handling of unsol_data_state_change with PDP sharing scenario" into honeycomb-LTE
This commit is contained in:
@@ -89,6 +89,11 @@ public class ApnContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setDataConnectionAc(DataConnectionAc dcac) {
|
public synchronized void setDataConnectionAc(DataConnectionAc dcac) {
|
||||||
|
if (dcac != null) {
|
||||||
|
dcac.addApnContext(this);
|
||||||
|
} else {
|
||||||
|
if (mDataConnectionAc != null) mDataConnectionAc.removeApnContext(this);
|
||||||
|
}
|
||||||
mDataConnectionAc = dcac;
|
mDataConnectionAc = dcac;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,18 @@ import android.net.LinkProperties;
|
|||||||
import android.net.ProxyProperties;
|
import android.net.ProxyProperties;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AsyncChannel to a DataConnection
|
* AsyncChannel to a DataConnection
|
||||||
*/
|
*/
|
||||||
public class DataConnectionAc extends AsyncChannel {
|
public class DataConnectionAc extends AsyncChannel {
|
||||||
private static final boolean DBG = false;
|
private static final boolean DBG = false;
|
||||||
private String mLogTag;
|
private String mLogTag;
|
||||||
|
private List<ApnContext> mApnList = null;
|
||||||
|
|
||||||
public DataConnection dataConnection;
|
public DataConnection dataConnection;
|
||||||
|
|
||||||
@@ -85,6 +91,7 @@ public class DataConnectionAc extends AsyncChannel {
|
|||||||
public DataConnectionAc(DataConnection dc, String logTag) {
|
public DataConnectionAc(DataConnection dc, String logTag) {
|
||||||
dataConnection = dc;
|
dataConnection = dc;
|
||||||
mLogTag = logTag;
|
mLogTag = logTag;
|
||||||
|
mApnList = Collections.synchronizedList(new ArrayList<ApnContext>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -371,6 +378,35 @@ public class DataConnectionAc extends AsyncChannel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add ApnContext association.
|
||||||
|
*
|
||||||
|
* @param ApnContext to associate
|
||||||
|
*/
|
||||||
|
public void addApnContext(ApnContext apnContext) {
|
||||||
|
if (!mApnList.contains(apnContext)) {
|
||||||
|
mApnList.add(apnContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove ApnContext associateion.
|
||||||
|
*
|
||||||
|
* @param ApnContext to dissociate
|
||||||
|
*/
|
||||||
|
public void removeApnContext(ApnContext apnContext) {
|
||||||
|
mApnList.remove(apnContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve collection of ApnContext currently associated with the DataConnectionAc.
|
||||||
|
*
|
||||||
|
* @return Collection of ApnContext
|
||||||
|
*/
|
||||||
|
public Collection<ApnContext> getApnList() {
|
||||||
|
return mApnList;
|
||||||
|
}
|
||||||
|
|
||||||
private void log(String s) {
|
private void log(String s) {
|
||||||
android.util.Log.d(mLogTag, "DataConnectionAc " + s);
|
android.util.Log.d(mLogTag, "DataConnectionAc " + s);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,9 @@ import java.net.InetAddress;
|
|||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
@@ -968,6 +970,49 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cid Connection id provided from RIL.
|
||||||
|
* @return DataConnectionAc associated with specified cid.
|
||||||
|
*/
|
||||||
|
private DataConnectionAc findDataConnectionAcByCid(int cid) {
|
||||||
|
for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
|
||||||
|
if (dcac.getCidSync() == cid) {
|
||||||
|
return dcac;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param dcacs Collection of DataConnectionAc reported from RIL.
|
||||||
|
* @return List of ApnContext whihc is connected, but does not present in
|
||||||
|
* data connection list reported from RIL.
|
||||||
|
*/
|
||||||
|
private List<ApnContext> findApnContextToClean(Collection<DataConnectionAc> dcacs) {
|
||||||
|
if (dcacs == null) return null;
|
||||||
|
|
||||||
|
ArrayList<ApnContext> list = new ArrayList<ApnContext>();
|
||||||
|
for (ApnContext apnContext : mApnContexts.values()) {
|
||||||
|
if (apnContext.getState() == State.CONNECTED) {
|
||||||
|
boolean found = false;
|
||||||
|
for (DataConnectionAc dcac : dcacs) {
|
||||||
|
if (dcac == apnContext.getDataConnectionAc()) {
|
||||||
|
// ApnContext holds the ref to dcac present in data call list.
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
// ApnContext does not have dcan reorted in data call list.
|
||||||
|
if (DBG) log("onDataStateChanged(ar): Connected apn not found in the list (" +
|
||||||
|
apnContext.toString() + ")");
|
||||||
|
list.add(apnContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ar is the result of RIL_REQUEST_DATA_CALL_LIST
|
* @param ar is the result of RIL_REQUEST_DATA_CALL_LIST
|
||||||
* or RIL_UNSOL_DATA_CALL_LIST_CHANGED
|
* or RIL_UNSOL_DATA_CALL_LIST_CHANGED
|
||||||
@@ -985,91 +1030,103 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
|
|||||||
if (DBG) log("onDataStateChanged(ar): exception; likely radio not available, ignore");
|
if (DBG) log("onDataStateChanged(ar): exception; likely radio not available, ignore");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (DBG) log("onDataStateChanged(ar): DataCallState size=" + dataCallStates.size());
|
||||||
|
|
||||||
// Create a hash map to store the dataCallState of each call id
|
// Create a hash map to store the dataCallState of each DataConnectionAc
|
||||||
// TODO: Depends on how frequent the DATA_CALL_LIST got updated,
|
// TODO: Depends on how frequent the DATA_CALL_LIST got updated,
|
||||||
// may cache response to reduce comparison.
|
// may cache response to reduce comparison.
|
||||||
HashMap<Integer, DataCallState> response;
|
HashMap<DataCallState, DataConnectionAc> response;
|
||||||
response = new HashMap<Integer, DataCallState>();
|
response = new HashMap<DataCallState, DataConnectionAc>();
|
||||||
if (DBG) log("onDataStateChanged(ar): DataCallState size=" + dataCallStates.size());
|
for (DataCallState dataCallState : dataCallStates) {
|
||||||
for (DataCallState dc : dataCallStates) {
|
DataConnectionAc dcac = findDataConnectionAcByCid(dataCallState.cid);
|
||||||
response.put(dc.cid, dc);
|
|
||||||
if (DBG) log("onDataStateChanged(ar): " + dc.cid + ": " + dc.toString());
|
if (dcac != null) response.put(dataCallState, dcac);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each connected apn, check if there is a matched active
|
// step1: Find a list of "connected" APN which does not have reference to
|
||||||
// data call state, which has the same link properties.
|
// calls listed in the Data Call List.
|
||||||
if (DBG) log(" ApnContext size=" + mApnContexts.values().size());
|
List<ApnContext> apnsToClear = findApnContextToClean(response.values());
|
||||||
for (ApnContext apnContext : mApnContexts.values()) {
|
|
||||||
if (DBG){
|
// step2: Check status of each calls in Data Call List.
|
||||||
log("onDataStateChanged(ar): " + apnContext.toString());
|
// Collect list of ApnContext associated with the data call if the link
|
||||||
if (apnContext.getDataConnection() != null) {
|
// has to be cleared.
|
||||||
log("onDataStateChanged(ar): " + apnContext.getDataConnection().toString());
|
for (DataCallState newState : dataCallStates) {
|
||||||
|
DataConnectionAc dcac = response.get(newState);
|
||||||
|
|
||||||
|
// no associated DataConnection found. Ignore.
|
||||||
|
if (dcac == null) continue;
|
||||||
|
|
||||||
|
Collection<ApnContext> apns = dcac.getApnList();
|
||||||
|
|
||||||
|
// filter out ApnContext with "Connected" state.
|
||||||
|
ArrayList<ApnContext> connectedApns = new ArrayList<ApnContext>();
|
||||||
|
for (ApnContext apnContext : apns) {
|
||||||
|
if ((apnContext != null) &&
|
||||||
|
(apnContext.getState() == State.CONNECTED)) {
|
||||||
|
connectedApns.add(apnContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataConnectionAc dcac = apnContext.getDataConnectionAc();
|
|
||||||
if (dcac == null) {
|
// No "Connected" ApnContext associated with this CID. Ignore.
|
||||||
|
if (connectedApns.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int connectionId = dcac.getCidSync();
|
|
||||||
|
|
||||||
if (apnContext.getState() == State.CONNECTED) {
|
if (DBG) log("onDataStateChanged(ar): Found ConnId=" + newState.cid
|
||||||
// The way things are supposed to work, the PDP list
|
|
||||||
// should not contain the CID after it disconnects.
|
|
||||||
// However, the way things really work, sometimes the PDP
|
|
||||||
// context is still listed with active = false, which
|
|
||||||
// makes it hard to distinguish an activating context from
|
|
||||||
// an activated-and-then de-activated one.
|
|
||||||
if (response.containsKey(connectionId)) {
|
|
||||||
DataCallState newState = response.get(connectionId);
|
|
||||||
if (DBG) log("onDataStateChanged(ar): Found ConnId=" + connectionId
|
|
||||||
+ " newState=" + newState.toString());
|
+ " newState=" + newState.toString());
|
||||||
if (newState.active != 0) {
|
if (newState.active != 0) {
|
||||||
boolean resetConnection;
|
boolean resetConnection;
|
||||||
switch (dcac.updateLinkPropertiesDataCallStateSync(newState)) {
|
switch (dcac.updateLinkPropertiesDataCallStateSync(newState)) {
|
||||||
case NONE:
|
case NONE:
|
||||||
if (DBG) log("onDataStateChanged(ar): Found but no change, skip");
|
if (DBG) log("onDataStateChanged(ar): Found but no change, skip");
|
||||||
resetConnection = false;
|
resetConnection = false;
|
||||||
break;
|
break;
|
||||||
case CHANGED:
|
case CHANGED:
|
||||||
if (DBG) log("onDataStateChanged(ar): Found and changed, notify");
|
for (ApnContext apnContext : connectedApns) {
|
||||||
mPhone.notifyDataConnection(Phone.REASON_LINK_PROPERTIES_CHANGED,
|
if (DBG) log("onDataStateChanged(ar): Found and changed, notify (" +
|
||||||
apnContext.getApnType());
|
apnContext.toString() + ")");
|
||||||
// Temporary hack, at this time a transition from CDMA -> Global
|
mPhone.notifyDataConnection(Phone.REASON_LINK_PROPERTIES_CHANGED,
|
||||||
// fails so we'll hope for the best and not reset the connection.
|
apnContext.getApnType());
|
||||||
// @see bug/4455071
|
|
||||||
if (SystemProperties.getBoolean("telephony.ignore-state-changes",
|
|
||||||
true)) {
|
|
||||||
log("onDataStateChanged(ar): STOPSHIP don't reset, continue");
|
|
||||||
resetConnection = false;
|
|
||||||
} else {
|
|
||||||
// Things changed so reset connection, when hack is removed
|
|
||||||
// this is the normal path.
|
|
||||||
log("onDataStateChanged(ar): changed so resetting connection");
|
|
||||||
resetConnection = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RESET:
|
|
||||||
default:
|
|
||||||
if (DBG) log("onDataStateChanged(ar): an error, reset connection");
|
|
||||||
resetConnection = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (resetConnection == false) continue;
|
|
||||||
}
|
}
|
||||||
|
// Temporary hack, at this time a transition from CDMA -> Global
|
||||||
|
// fails so we'll hope for the best and not reset the connection.
|
||||||
|
// @see bug/4455071
|
||||||
|
if (SystemProperties.getBoolean("telephony.ignore-state-changes",
|
||||||
|
true)) {
|
||||||
|
log("onDataStateChanged(ar): STOPSHIP don't reset, continue");
|
||||||
|
resetConnection = false;
|
||||||
|
} else {
|
||||||
|
// Things changed so reset connection, when hack is removed
|
||||||
|
// this is the normal path.
|
||||||
|
log("onDataStateChanged(ar): changed so resetting connection");
|
||||||
|
resetConnection = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RESET:
|
||||||
|
default:
|
||||||
|
if (DBG) log("onDataStateChanged(ar): an error, reset connection");
|
||||||
|
resetConnection = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
if (resetConnection == false) continue;
|
||||||
if (DBG) log("onDataStateChanged(ar): reset connection.");
|
|
||||||
|
|
||||||
// Add an event log when the network drops PDP
|
|
||||||
int cid = getCellLocationId();
|
|
||||||
EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid,
|
|
||||||
TelephonyManager.getDefault().getNetworkType());
|
|
||||||
|
|
||||||
cleanUpConnection(true, apnContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DBG) log("onDataStateChanged(ar): reset connection.");
|
||||||
|
|
||||||
|
apnsToClear.addAll(connectedApns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// step3: Clear apn connection if applicable.
|
||||||
|
if (!apnsToClear.isEmpty()) {
|
||||||
|
// Add an event log when the network drops PDP
|
||||||
|
int cid = getCellLocationId();
|
||||||
|
EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid,
|
||||||
|
TelephonyManager.getDefault().getNetworkType());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ApnContext apnContext : apnsToClear) {
|
||||||
|
cleanUpConnection(true, apnContext);
|
||||||
|
}
|
||||||
if (DBG) log("onDataStateChanged(ar): X");
|
if (DBG) log("onDataStateChanged(ar): X");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user