From e303d7b31800f1382982cd0117b44ecfbdec08c4 Mon Sep 17 00:00:00 2001 From: Kazuhiro Ondo Date: Sun, 19 Jun 2011 13:19:59 -0500 Subject: Fix handling of unsol_data_state_change with PDP sharing scenario The original logic was to go through each ApnContext to find changes on the DataConnection if applicable. This was causing an issue when an DC is shared by multiple ApnContexts. Only the first ApnContext associated with the updated DC was detecting the link properties change. The change in this patch is to go through the changes by DC instead of ApnContext. And make sure the update on a DC is propagated to all associated ApnContexts. Bug: 4744006 Change-Id: Ie52d62d1d5671005f9e74b1ddc24822c9013e3e4 --- .../com/android/internal/telephony/ApnContext.java | 5 + .../internal/telephony/DataConnectionAc.java | 36 ++++ .../telephony/gsm/GsmDataConnectionTracker.java | 191 +++++++++++++-------- 3 files changed, 165 insertions(+), 67 deletions(-) diff --git a/telephony/java/com/android/internal/telephony/ApnContext.java b/telephony/java/com/android/internal/telephony/ApnContext.java index 496c43c5cd55..5ec00e834e9e 100644 --- a/telephony/java/com/android/internal/telephony/ApnContext.java +++ b/telephony/java/com/android/internal/telephony/ApnContext.java @@ -89,6 +89,11 @@ public class ApnContext { } public synchronized void setDataConnectionAc(DataConnectionAc dcac) { + if (dcac != null) { + dcac.addApnContext(this); + } else { + if (mDataConnectionAc != null) mDataConnectionAc.removeApnContext(this); + } mDataConnectionAc = dcac; } diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java index 62b90aef26cb..e23f1ccc3b8e 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java @@ -24,12 +24,18 @@ import android.net.LinkProperties; import android.net.ProxyProperties; import android.os.Message; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + /** * AsyncChannel to a DataConnection */ public class DataConnectionAc extends AsyncChannel { private static final boolean DBG = false; private String mLogTag; + private List mApnList = null; public DataConnection dataConnection; @@ -85,6 +91,7 @@ public class DataConnectionAc extends AsyncChannel { public DataConnectionAc(DataConnection dc, String logTag) { dataConnection = dc; mLogTag = logTag; + mApnList = Collections.synchronizedList(new ArrayList()); } /** @@ -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 getApnList() { + return mApnList; + } + private void log(String s) { android.util.Log.d(mLogTag, "DataConnectionAc " + s); } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 645bc69b2160..e11188807313 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -68,7 +68,9 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; +import java.util.List; import java.util.Map; 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 findApnContextToClean(Collection dcacs) { + if (dcacs == null) return null; + + ArrayList list = new ArrayList(); + 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 * 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"); 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, // may cache response to reduce comparison. - HashMap response; - response = new HashMap(); - if (DBG) log("onDataStateChanged(ar): DataCallState size=" + dataCallStates.size()); - for (DataCallState dc : dataCallStates) { - response.put(dc.cid, dc); - if (DBG) log("onDataStateChanged(ar): " + dc.cid + ": " + dc.toString()); + HashMap response; + response = new HashMap(); + for (DataCallState dataCallState : dataCallStates) { + DataConnectionAc dcac = findDataConnectionAcByCid(dataCallState.cid); + + if (dcac != null) response.put(dataCallState, dcac); } - // For each connected apn, check if there is a matched active - // data call state, which has the same link properties. - if (DBG) log(" ApnContext size=" + mApnContexts.values().size()); - for (ApnContext apnContext : mApnContexts.values()) { - if (DBG){ - log("onDataStateChanged(ar): " + apnContext.toString()); - if (apnContext.getDataConnection() != null) { - log("onDataStateChanged(ar): " + apnContext.getDataConnection().toString()); + // step1: Find a list of "connected" APN which does not have reference to + // calls listed in the Data Call List. + List apnsToClear = findApnContextToClean(response.values()); + + // step2: Check status of each calls in Data Call List. + // Collect list of ApnContext associated with the data call if the link + // has to be cleared. + for (DataCallState newState : dataCallStates) { + DataConnectionAc dcac = response.get(newState); + + // no associated DataConnection found. Ignore. + if (dcac == null) continue; + + Collection apns = dcac.getApnList(); + + // filter out ApnContext with "Connected" state. + ArrayList connectedApns = new ArrayList(); + 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; } - int connectionId = dcac.getCidSync(); - if (apnContext.getState() == State.CONNECTED) { - // 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 + if (DBG) log("onDataStateChanged(ar): Found ConnId=" + newState.cid + " newState=" + newState.toString()); - if (newState.active != 0) { - boolean resetConnection; - switch (dcac.updateLinkPropertiesDataCallStateSync(newState)) { - case NONE: - if (DBG) log("onDataStateChanged(ar): Found but no change, skip"); - resetConnection = false; - break; - case CHANGED: - if (DBG) log("onDataStateChanged(ar): Found and changed, notify"); - mPhone.notifyDataConnection(Phone.REASON_LINK_PROPERTIES_CHANGED, - apnContext.getApnType()); - // 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 (newState.active != 0) { + boolean resetConnection; + switch (dcac.updateLinkPropertiesDataCallStateSync(newState)) { + case NONE: + if (DBG) log("onDataStateChanged(ar): Found but no change, skip"); + resetConnection = false; + break; + case CHANGED: + for (ApnContext apnContext : connectedApns) { + if (DBG) log("onDataStateChanged(ar): Found and changed, notify (" + + apnContext.toString() + ")"); + mPhone.notifyDataConnection(Phone.REASON_LINK_PROPERTIES_CHANGED, + apnContext.getApnType()); } + // 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."); + 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()); + apnsToClear.addAll(connectedApns); + } - cleanUpConnection(true, apnContext); - } + // 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"); } -- cgit v1.2.3-59-g8ed1b