Add single-pdn arbitration.
If a device or network can only support a single data connection
then we need to tear down lower priority connections in favor
of higher priority requests.
bug:10212445
Change-Id: I5ff38ae3be993aee51b6716045e6857b4f2942af
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 20927b9..f4e26ea 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -103,6 +103,7 @@
static final String REASON_DATA_DEPENDENCY_UNMET = "dependencyUnmet";
static final String REASON_LOST_DATA_CONNECTION = "lostDataConnection";
static final String REASON_CONNECTED = "connected";
+ static final String REASON_SINGLE_PDN_ARBITRATION = "SinglePdnArbitration";
// Used for band mode selection methods
static final int BM_UNSPECIFIED = 0; // selected by baseband automatically
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
index 312d033..16aad87 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.content.Context;
+import android.net.NetworkConfig;
import android.telephony.Rlog;
import com.android.internal.R;
@@ -47,6 +48,8 @@
private ArrayList<ApnSetting> mWaitingApns = null;
+ public final int priority;
+
/** A zero indicates that all waiting APNs had a permanent error */
private AtomicInteger mWaitingApnsPermanentFailureCountDown;
@@ -68,14 +71,15 @@
*/
AtomicBoolean mDependencyMet;
- public ApnContext(Context context, String apnType, String logTag) {
+ public ApnContext(Context context, String apnType, String logTag, NetworkConfig config) {
mContext = context;
mApnType = apnType;
mState = DctConstants.State.IDLE;
setReason(Phone.REASON_DATA_ENABLED);
mDataEnabled = new AtomicBoolean(false);
- mDependencyMet = new AtomicBoolean(true);
+ mDependencyMet = new AtomicBoolean(config.dependencyMet);
mWaitingApnsPermanentFailureCountDown = new AtomicInteger(0);
+ priority = config.priority;
LOG_TAG = logTag;
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 15c75b0..ae026b7 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -35,6 +35,7 @@
import android.net.ProxyProperties;
import android.net.Uri;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Message;
import android.os.Messenger;
import android.os.SystemClock;
@@ -190,6 +191,7 @@
mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver);
mApnContexts.clear();
+ mPrioritySortedApnContexts.clear();
destroyDataConnections();
}
@@ -230,9 +232,9 @@
}
private ApnContext addApnContext(String type, NetworkConfig networkConfig) {
- ApnContext apnContext = new ApnContext(mPhone.getContext(), type, LOG_TAG);
- apnContext.setDependencyMet(networkConfig.dependencyMet);
+ ApnContext apnContext = new ApnContext(mPhone.getContext(), type, LOG_TAG, networkConfig);
mApnContexts.put(type, apnContext);
+ mPrioritySortedApnContexts.add(apnContext);
return apnContext;
}
@@ -596,7 +598,10 @@
}
private void setupDataOnConnectableApns(String reason) {
- for (ApnContext apnContext : mApnContexts.values()) {
+ if (DBG) log("setupDataOnConnectableApns: " + reason);
+
+ for (ApnContext apnContext : mPrioritySortedApnContexts) {
+ if (DBG) log("setupDataOnConnectableApns: apnContext " + apnContext);
if (apnContext.getState() == DctConstants.State.FAILED) {
apnContext.setState(DctConstants.State.IDLE);
}
@@ -608,28 +613,6 @@
}
}
- private boolean trySetupData(String reason, String type) {
- if (DBG) {
- log("trySetupData: " + type + " due to " + (reason == null ? "(unspecified)" : reason)
- + " isPsRestricted=" + mIsPsRestricted);
- }
-
- if (type == null) {
- type = PhoneConstants.APN_TYPE_DEFAULT;
- }
-
- ApnContext apnContext = mApnContexts.get(type);
-
- if (apnContext == null ){
- if (DBG) log("trySetupData new apn context for type:" + type);
- apnContext = new ApnContext(mPhone.getContext(), type, LOG_TAG);
- mApnContexts.put(type, apnContext);
- }
- apnContext.setReason(reason);
-
- return trySetupData(apnContext);
- }
-
private boolean trySetupData(ApnContext apnContext) {
if (DBG) {
log("trySetupData for type:" + apnContext.getApnType() +
@@ -720,11 +703,16 @@
* @param tearDown true if the underlying DataConnection should be
* disconnected.
* @param reason reason for the clean up.
+ * @return boolean - true if we did cleanup any connections, false if they
+ * were already all disconnected.
*/
- protected void cleanUpAllConnections(boolean tearDown, String reason) {
+ protected boolean cleanUpAllConnections(boolean tearDown, String reason) {
if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason);
+ boolean didDisconnect = false;
for (ApnContext apnContext : mApnContexts.values()) {
+ if (apnContext.isDisconnected() == false) didDisconnect = true;
+ // TODO - only do cleanup if not disconnected
apnContext.setReason(reason);
cleanUpConnection(tearDown, apnContext);
}
@@ -734,6 +722,7 @@
// TODO: Do we need mRequestedApnType?
mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
+ return didDisconnect;
}
/**
@@ -1033,6 +1022,29 @@
}
}
if (dcac == null) {
+ if (isOnlySingleDcAllowed(radioTech)) {
+ if (isHigherPriorityApnContextActive(apnContext)) {
+ if (DBG) {
+ log("setupData: Higher priority ApnContext active. Ignoring call");
+ }
+ return false;
+ }
+
+ // Only lower priority calls left. Disconnect them all in this single PDP case
+ // so that we can bring up the requested higher priority call (once we receive
+ // repsonse for deactivate request for the calls we are about to disconnect
+ if (cleanUpAllConnections(true, Phone.REASON_SINGLE_PDN_ARBITRATION)) {
+ // If any call actually requested to be disconnected, means we can't
+ // bring up this connection yet as we need to wait for those data calls
+ // to be disconnected.
+ if (DBG) log("setupData: Some calls are disconnecting first. Wait and retry");
+ return false;
+ }
+
+ // No other calls are active, so proceed
+ if (DBG) log("setupData: Single pdp. Continue setting up data call.");
+ }
+
dcac = findFreeDataConnection();
if (dcac == null) {
@@ -1166,6 +1178,45 @@
mActiveApn = null;
}
+ /**
+ * "Active" here means ApnContext isEnabled() and not in FAILED state
+ * @param apnContext to compare with
+ * @return true if higher priority active apn found
+ */
+ private boolean isHigherPriorityApnContextActive(ApnContext apnContext) {
+ for (ApnContext otherContext : mPrioritySortedApnContexts) {
+ if (apnContext.getApnType().equalsIgnoreCase(otherContext.getApnType())) return false;
+ if (otherContext.isEnabled() && otherContext.getState() != DctConstants.State.FAILED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Reports if we support multiple connections or not.
+ * This is a combination of factors, based on carrier and RAT.
+ * @param rilRadioTech the RIL Radio Tech currently in use
+ * @return true if only single DataConnection is allowed
+ */
+ private boolean isOnlySingleDcAllowed(int rilRadioTech) {
+ int[] singleDcRats = mPhone.getContext().getResources().getIntArray(
+ com.android.internal.R.array.config_onlySingleDcAllowed);
+ boolean onlySingleDcAllowed = false;
+ if (Build.IS_DEBUGGABLE &&
+ SystemProperties.getBoolean("persist.telephony.test.singleDc", false)) {
+ onlySingleDcAllowed = true;
+ }
+ if (singleDcRats != null) {
+ for (int i=0; i < singleDcRats.length && onlySingleDcAllowed == false; i++) {
+ if (rilRadioTech == singleDcRats[i]) onlySingleDcAllowed = true;
+ }
+ }
+
+ if (DBG) log("isOnlySingleDcAllowed(" + rilRadioTech + "): " + onlySingleDcAllowed);
+ return onlySingleDcAllowed;
+ }
+
@Override
protected void restartRadio() {
if (DBG) log("restartRadio: ************TURN OFF RADIO**************");
@@ -1190,10 +1241,13 @@
* @param reason the reason why data is disconnected
* @return true if try setup data connection is need for this reason
*/
- private boolean retryAfterDisconnected(String reason) {
+ private boolean retryAfterDisconnected(ApnContext apnContext) {
boolean retry = true;
+ String reason = apnContext.getReason();
- if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) {
+ if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ||
+ (isOnlySingleDcAllowed(mPhone.getServiceState().getRilDataRadioTechnology())
+ && isHigherPriorityApnContextActive(apnContext))) {
retry = false;
}
return retry;
@@ -1764,8 +1818,7 @@
}
// If APN is still enabled, try to bring it back up automatically
- if (mAttached.get() && apnContext.isReady()
- && retryAfterDisconnected(apnContext.getReason())) {
+ if (mAttached.get() && apnContext.isReady() && retryAfterDisconnected(apnContext)) {
SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "false");
// Wait a bit before trying the next APN, so that
// we're not tying up the RIL command channel.
@@ -1774,6 +1827,9 @@
} else {
apnContext.setApnSetting(null);
apnContext.setDataConnectionAc(null);
+ if (isOnlySingleDcAllowed(mPhone.getServiceState().getRilDataRadioTechnology())) {
+ setupDataOnConnectableApns(Phone.REASON_SINGLE_PDN_ARBITRATION);
+ }
}
}
@@ -2183,7 +2239,16 @@
cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
mReregisterOnReconnectFailure = false;
}
- trySetupData(Phone.REASON_PS_RESTRICT_ENABLED, PhoneConstants.APN_TYPE_DEFAULT);
+ ApnContext apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_DEFAULT);
+ if (apnContext != null) {
+ apnContext.setReason(Phone.REASON_PS_RESTRICT_ENABLED);
+ trySetupData(apnContext);
+ } else {
+ loge("**** Default ApnContext not found ****");
+ if (Build.IS_DEBUGGABLE) {
+ throw new RuntimeException("Default ApnContext not found");
+ }
+ }
}
break;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java b/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
index 9e7702e..4a4433b 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
@@ -63,12 +63,14 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.PriorityQueue;
/**
* {@hide}
@@ -251,9 +253,19 @@
new HashMap<String, Integer>();
/** Phone.APN_TYPE_* ===> ApnContext */
- protected ConcurrentHashMap<String, ApnContext> mApnContexts =
+ protected final ConcurrentHashMap<String, ApnContext> mApnContexts =
new ConcurrentHashMap<String, ApnContext>();
+ /** kept in sync with mApnContexts
+ * Higher numbers are higher priority and sorted so highest priority is first */
+ protected final PriorityQueue<ApnContext>mPrioritySortedApnContexts =
+ new PriorityQueue<ApnContext>(5,
+ new Comparator<ApnContext>() {
+ public int compare(ApnContext c1, ApnContext c2) {
+ return c2.priority - c1.priority;
+ }
+ } );
+
/* Currently active APN */
protected ApnSetting mActiveApn;