Loading src/java/com/android/internal/telephony/dataconnection/ApnContext.java +9 −5 Original line number Diff line number Diff line Loading @@ -460,7 +460,11 @@ public class ApnContext { } } public boolean hasNoRestrictedRequests(boolean excludeDun) { /** * @param excludeDun True if excluding requests that have DUN capability * @return True if the attached network requests contain restricted capability. */ public boolean hasRestrictedRequests(boolean excludeDun) { synchronized (mRefCountLock) { for (NetworkRequest nr : mNetworkRequests) { if (excludeDun && Loading @@ -468,13 +472,13 @@ public class ApnContext { NetworkCapabilities.NET_CAPABILITY_DUN)) { continue; } if (nr.networkCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) == false) { return false; if (!nr.networkCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) { return true; } } } return true; return false; } private final SparseIntArray mRetriesLeftPerErrorCode = new SparseIntArray(); Loading src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java +4 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.telephony.CarrierConfigManager; import android.telephony.Rlog; import android.telephony.ServiceState; import android.telephony.data.ApnSetting; import android.text.TextUtils; import android.util.Log; import com.android.internal.telephony.Phone; Loading Loading @@ -118,7 +119,7 @@ public class ApnSettingUtils { * @return {@code true} if the APN type is metered, {@code false} otherwise. */ public static boolean isMeteredApnType(String type, Phone phone) { if (phone == null) { if (phone == null || TextUtils.isEmpty(type)) { return true; } Loading Loading @@ -193,11 +194,12 @@ public class ApnSettingUtils { /** * Check if this APN setting is metered. * * @param apn APN setting * @param phone The phone object * @return True if this APN setting is metered, otherwise false. */ public static boolean isMetered(ApnSetting apn, Phone phone) { if (phone == null) { if (phone == null || apn == null) { return true; } Loading src/java/com/android/internal/telephony/dataconnection/DataConnection.java +136 −51 Original line number Diff line number Diff line Loading @@ -123,17 +123,14 @@ public class DataConnection extends StateMachine { ApnContext mApnContext; int mProfileId; int mRilRat; final boolean mUnmeteredUseOnly; Message mOnCompletedMsg; final int mConnectionGeneration; ConnectionParams(ApnContext apnContext, int profileId, int rilRadioTechnology, boolean unmeteredUseOnly, Message onCompletedMsg, int connectionGeneration) { Message onCompletedMsg, int connectionGeneration) { mApnContext = apnContext; mProfileId = profileId; mRilRat = rilRadioTechnology; mUnmeteredUseOnly = unmeteredUseOnly; mOnCompletedMsg = onCompletedMsg; mConnectionGeneration = connectionGeneration; } Loading @@ -143,7 +140,6 @@ public class DataConnection extends StateMachine { return "{mTag=" + mTag + " mApnContext=" + mApnContext + " mProfileId=" + mProfileId + " mRat=" + mRilRat + " mUnmeteredUseOnly=" + mUnmeteredUseOnly + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) + "}"; } } Loading Loading @@ -222,8 +218,9 @@ public class DataConnection extends StateMachine { static final int EVENT_KEEPALIVE_STOP_REQUEST = BASE + 22; static final int EVENT_LINK_CAPACITY_CHANGED = BASE + 23; static final int EVENT_RESET = BASE + 24; static final int EVENT_REEVALUATE_RESTRICTED_STATE = BASE + 25; private static final int CMD_TO_STRING_COUNT = EVENT_RESET - BASE + 1; private static final int CMD_TO_STRING_COUNT = EVENT_REEVALUATE_RESTRICTED_STATE - BASE + 1; private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT]; static { Loading Loading @@ -255,6 +252,8 @@ public class DataConnection extends StateMachine { sCmdToString[EVENT_KEEPALIVE_STOP_REQUEST - BASE] = "EVENT_KEEPALIVE_STOP_REQUEST"; sCmdToString[EVENT_LINK_CAPACITY_CHANGED - BASE] = "EVENT_LINK_CAPACITY_CHANGED"; sCmdToString[EVENT_RESET - BASE] = "EVENT_RESET"; sCmdToString[EVENT_REEVALUATE_RESTRICTED_STATE - BASE] = "EVENT_REEVALUATE_RESTRICTED_STATE"; } // Convert cmd to string or null if unknown static String cmdToString(int cmd) { Loading Loading @@ -735,6 +734,8 @@ public class DataConnection extends StateMachine { mLinkProperties = new LinkProperties(); mApnContexts.clear(); mApnSetting = null; mUnmeteredUseOnly = false; mRestrictedNetworkOverride = false; mDcFailCause = null; } Loading Loading @@ -885,14 +886,19 @@ public class DataConnection extends StateMachine { mLinkProperties.setTcpBufferSizes(sizes); } /** * Indicates if this data connection was established for unmetered use only. Note that this * flag should be populated when data becomes active. And if it is set to true, it can be set to * false later when we are reevaluating the data connection. But if it is set to false, it * can never become true later because setting it to true will cause this data connection * losing some immutable network capabilities, which can cause issues in connectivity service. */ private boolean mUnmeteredUseOnly = false; /** * Indicates if when this connection was established we had a restricted/privileged * NetworkRequest and needed it to overcome data-enabled limitations. * * This gets set once per connection setup and is based on conditions at that time. * We could theoretically have dynamic capabilities but now is not a good time to * experiment with that. * * This flag overrides the APN-based restriction capability, restricting the network * based on both having a NetworkRequest with restricted AND needing a restricted * bit to overcome user-disabled status. This allows us to handle the common case Loading @@ -900,38 +906,78 @@ public class DataConnection extends StateMachine { * if conditions require a restricted network to overcome user-disabled then it must * be restricted, otherwise it is unrestricted (or restricted based on APN type). * * Because we're not supporting dynamic capabilities, if conditions change and we go from * data-enabled to not or vice-versa we will need to tear down networks to deal with it * at connection setup time with the new state. * * This supports a privileged app bringing up a network without general apps having access * to it when the network is otherwise unavailable (hipri). The first use case is * pre-paid SIM reprovisioning over internet, where the carrier insists on no traffic * other than from the privileged carrier-app. * * Note that the data connection cannot go from unrestricted to restricted because the * connectivity service does not support dynamically closing TCP connections at this point. */ private boolean mRestrictedNetworkOverride = false; // Should be called once when the call goes active to examine the state of things and // declare the restriction override for the life of the connection private void setNetworkRestriction() { mRestrictedNetworkOverride = false; // first, if we have no restricted requests, this override can stay FALSE: boolean noRestrictedRequests = true; /** * Check if this data connection should be restricted. We should call this when data connection * becomes active, or when we want to re-evaluate the conditions to decide if we need to * unstrict the data connection. * * @return True if this data connection needs to be restricted. */ private boolean shouldRestrictNetwork() { // first, check if there is any network request that containing restricted capability // (i.e. Do not have NET_CAPABILITY_NOT_RESTRICTED in the request) boolean isAnyRestrictedRequest = false; for (ApnContext apnContext : mApnContexts.keySet()) { noRestrictedRequests &= apnContext.hasNoRestrictedRequests(true /* exclude DUN */); if (apnContext.hasRestrictedRequests(true /* exclude DUN */)) { isAnyRestrictedRequest = true; break; } if (noRestrictedRequests) { return; } // Do we need a restricted network to satisfy the request? // Is this network metered? If not, then don't add restricted // If all of the network requests are non-restricted, then we don't need to restrict // the network. if (!isAnyRestrictedRequest) { return false; } // If the network is unmetered, then we don't need to restrict the network because users // won't be charged anyway. if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) { return; return false; } // If the data is disabled, then we need to restrict the network so only privileged apps can // use the restricted network while data is disabled. if (!mDct.isDataEnabled()) { return true; } // Is data disabled? mRestrictedNetworkOverride = !mDct.isDataEnabled(); // If the device is roaming, and the user does not turn on data roaming, then we need to // restrict the network so only privileged apps can use it. if (!mDct.getDataRoamingEnabled() && mPhone.getServiceState().getDataRoaming()) { return true; } // Otherwise we should not restrict the network so anyone who requests can use it. return false; } /** * @return True if this data connection should only be used for unmetered purposes. */ private boolean isUnmeteredUseOnly() { // The data connection can only be unmetered used only if all requests' reasons are // unmetered. for (ApnContext apnContext : mApnContexts.keySet()) { DataConnectionReasons dataConnectionReasons = new DataConnectionReasons(); boolean isDataAllowed = mDct.isDataAllowed(apnContext, dataConnectionReasons); if (!isDataAllowed || !dataConnectionReasons.contains( DataConnectionReasons.DataAllowedReasonType.UNMETERED_APN)) { return false; } } return true; } NetworkCapabilities getNetworkCapabilities() { Loading @@ -942,8 +988,7 @@ public class DataConnection extends StateMachine { final String[] types = ApnSetting.getApnTypesStringFromBitmask( mApnSetting.getApnTypeBitmask()).split(","); for (String type : types) { if (!mRestrictedNetworkOverride && (mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly) if (!mRestrictedNetworkOverride && mUnmeteredUseOnly && ApnSettingUtils.isMeteredApnType(type, mPhone)) { log("Dropped the metered " + type + " for the unmetered data call."); continue; Loading Loading @@ -1003,8 +1048,7 @@ public class DataConnection extends StateMachine { // Mark NOT_METERED in the following cases, // 1. All APNs in APN settings are unmetered. // 2. The non-restricted data and is intended for unmetered use only. if (((mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly) && !mRestrictedNetworkOverride) if ((mUnmeteredUseOnly && !mRestrictedNetworkOverride) || !ApnSettingUtils.isMetered(mApnSetting, mPhone)) { result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); } else { Loading @@ -1013,6 +1057,7 @@ public class DataConnection extends StateMachine { result.maybeMarkCapabilitiesRestricted(); } if (mRestrictedNetworkOverride) { result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); // don't use dun on restriction-overriden networks. Loading Loading @@ -1270,17 +1315,11 @@ public class DataConnection extends StateMachine { break; case EVENT_DISCONNECT: if (DBG) { log("DcDefaultState deferring msg.what=EVENT_DISCONNECT RefCount=" + mApnContexts.size()); } deferMessage(msg); break; case EVENT_DISCONNECT_ALL: case EVENT_REEVALUATE_RESTRICTED_STATE: if (DBG) { log("DcDefaultState deferring msg.what=EVENT_DISCONNECT_ALL RefCount=" + mApnContexts.size()); log("DcDefaultState deferring msg.what=" + getWhatToString(msg.what) + " RefCount=" + mApnContexts.size()); } deferMessage(msg); break; Loading Loading @@ -1457,8 +1496,10 @@ public class DataConnection extends StateMachine { switch (msg.what) { case EVENT_RESET: case EVENT_REEVALUATE_RESTRICTED_STATE: if (DBG) { log("DcInactiveState: msg.what=EVENT_RESET, ignore we're already reset"); log("DcInactiveState: msg.what=" + getWhatToString(msg.what) + ", ignore we're already done"); } retVal = HANDLED; break; Loading Loading @@ -1659,8 +1700,13 @@ public class DataConnection extends StateMachine { } misc.subscriberId = mPhone.getSubscriberId(); setNetworkRestriction(); if (DBG) log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride); mRestrictedNetworkOverride = shouldRestrictNetwork(); mUnmeteredUseOnly = isUnmeteredUseOnly(); if (DBG) { log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride + ", mUnmeteredUseOnly = " + mUnmeteredUseOnly); } mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(), "DcNetworkAgent", mNetworkInfo, getNetworkCapabilities(), mLinkProperties, 50, misc); Loading Loading @@ -1924,6 +1970,37 @@ public class DataConnection extends StateMachine { retVal = HANDLED; break; } case EVENT_REEVALUATE_RESTRICTED_STATE: { // If the network was restricted, and now it does not need to be restricted // anymore, we should add the NET_CAPABILITY_NOT_RESTRICTED capability. if (mRestrictedNetworkOverride && !shouldRestrictNetwork()) { if (DBG) { log("Data connection becomes not-restricted. dc=" + this); } // Note we only do this when network becomes non-restricted. When a // non-restricted becomes restricted (e.g. users disable data, or turn off // data roaming), DCT will explicitly tear down the networks (because // connectivity service does not support force-close TCP connections today). // Also note that NET_CAPABILITY_NOT_RESTRICTED is an immutable capability // (see {@link NetworkCapabilities}) once we add it to the network, we can't // remove it through the entire life cycle of the connection. mRestrictedNetworkOverride = false; mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities()); } // If the data does need to be unmetered use only (e.g. users turn on data, or // device is not roaming anymore assuming data roaming is off), then we can // dynamically add those metered APN type capabilities back. (But not the // other way around because most of the APN-type capabilities are immutable // capabilities.) if (mUnmeteredUseOnly && !isUnmeteredUseOnly()) { mUnmeteredUseOnly = false; mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities()); } retVal = HANDLED; break; } default: if (VDBG) { log("DcActiveState not handled msg.what=" + getWhatToString(msg.what)); Loading Loading @@ -2259,7 +2336,6 @@ public class DataConnection extends StateMachine { * @param apnContext is the Access Point Name to bring up a connection to * @param profileId for the connection * @param rilRadioTechnology Radio technology for the data connection * @param unmeteredUseOnly Indicates the data connection can only used for unmetered purposes * @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(). Loading @@ -2267,15 +2343,13 @@ public class DataConnection extends StateMachine { * ignored if obsolete. */ public void bringUp(ApnContext apnContext, int profileId, int rilRadioTechnology, boolean unmeteredUseOnly, Message onCompletedMsg, int connectionGeneration) { Message onCompletedMsg, int connectionGeneration) { if (DBG) { log("bringUp: apnContext=" + apnContext + "unmeteredUseOnly=" + unmeteredUseOnly + " onCompletedMsg=" + onCompletedMsg); log("bringUp: apnContext=" + apnContext + " onCompletedMsg=" + onCompletedMsg); } sendMessage(DataConnection.EVENT_CONNECT, new ConnectionParams(apnContext, profileId, rilRadioTechnology, unmeteredUseOnly, onCompletedMsg, connectionGeneration)); new ConnectionParams(apnContext, profileId, rilRadioTechnology, onCompletedMsg, connectionGeneration)); } /** Loading Loading @@ -2324,6 +2398,15 @@ public class DataConnection extends StateMachine { if (DBG) log("reset"); } /** * Re-evaluate the restricted state. If the restricted data connection does not need to be * restricted anymore, we need to dynamically change the network's capability. */ void reevaluateRestrictedState() { sendMessage(EVENT_REEVALUATE_RESTRICTED_STATE); if (DBG) log("reevaluate restricted state"); } /** * @return The parameters used for initiating a data connection. */ Loading Loading @@ -2557,6 +2640,8 @@ public class DataConnection extends StateMachine { pw.println("mLastFailCause=" + mLastFailCause); pw.println("mUserData=" + mUserData); pw.println("mSubscriptionOverride=" + Integer.toHexString(mSubscriptionOverride)); pw.println("mRestrictedNetworkOverride=" + mRestrictedNetworkOverride); pw.println("mUnmeteredUseOnly=" + mUnmeteredUseOnly); pw.println("mInstanceNumber=" + mInstanceNumber); pw.println("mAc=" + mAc); pw.println("Network capabilities changed history:"); Loading src/java/com/android/internal/telephony/dataconnection/DcTracker.java +22 −46 Original line number Diff line number Diff line Loading @@ -818,47 +818,16 @@ public class DcTracker extends Handler { * * For example, handle reverting restricted networks back to unrestricted. If we're changing * user data to enabled and this makes data truly enabled (not disabled by other factors) we * need to tear down any metered apn type that was enabled anyway by a privileged request. * This allows us to reconnect to it in an unrestricted way. * need to reevaluate and possibly add NET_CAPABILITY_NOT_RESTRICTED capability to the data * connection. This allows non-privilege apps to use the network. * * Or when we brought up a unmetered data connection while data is off, we only limit this * data connection for unmetered use only. When data is turned back on, we need to tear that * down so a full capable data connection can be re-established. */ private void reevaluateDataConnections() { if (mDataEnabledSettings.isDataEnabled()) { for (ApnContext apnContext : mApnContexts.values()) { if (apnContext.isConnectedOrConnecting()) { final DataConnection dataConnection = apnContext.getDataConnection(); if (dataConnection != null) { final NetworkCapabilities netCaps = dataConnection.getNetworkCapabilities(); if (netCaps != null && !netCaps.hasCapability(NetworkCapabilities .NET_CAPABILITY_NOT_RESTRICTED) && !netCaps.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) { if (DBG) { log("Tearing down restricted metered net:" + apnContext); } // Tearing down the restricted metered data call when // conditions change. This will allow reestablishing a new unrestricted // data connection. apnContext.setReason(Phone.REASON_DATA_ENABLED); cleanUpConnection(true, apnContext); } else if (ApnSettingUtils.isMetered(apnContext.getApnSetting(), mPhone) && (netCaps != null && netCaps.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_METERED))) { if (DBG) { log("Tearing down unmetered net:" + apnContext); } // The APN settings is metered, but the data was still marked as // unmetered data, must be the unmetered data connection brought up when // data is off. We need to tear that down when data is enabled again. // This will allow reestablishing a new full capability data connection. apnContext.setReason(Phone.REASON_DATA_ENABLED); cleanUpConnection(true, apnContext); } } } } for (DataConnection dataConnection : mDataConnections.values()) { dataConnection.reevaluateRestrictedState(); } } Loading Loading @@ -1244,7 +1213,8 @@ public class DcTracker extends Handler { * provided. * @return True if data connection is allowed, otherwise false. */ boolean isDataAllowed(ApnContext apnContext, DataConnectionReasons dataConnectionReasons) { public boolean isDataAllowed(ApnContext apnContext, DataConnectionReasons dataConnectionReasons) { // Step 1: Get all environment conditions. // Step 2: Special handling for emergency APN. // Step 3. Build disallowed reasons. Loading Loading @@ -1367,11 +1337,11 @@ public class DcTracker extends Handler { reasons.add(DataAllowedReasonType.UNMETERED_APN); } // If the request is restricted and there are only disallowed reasons due to data // disabled, we should allow the data. // If the request is restricted and there are only soft disallowed reasons (e.g. data // disabled, data roaming disabled) existing, we should allow the data. if (apnContext != null && !apnContext.hasNoRestrictedRequests(true) && reasons.contains(DataDisallowedReasonType.DATA_DISABLED)) { && apnContext.hasRestrictedRequests(true) && !reasons.allowed()) { reasons.add(DataAllowedReasonType.RESTRICTED_REQUEST); } Loading Loading @@ -1491,8 +1461,7 @@ public class DcTracker extends Handler { } } boolean retValue = setupData(apnContext, radioTech, dataConnectionReasons.contains( DataAllowedReasonType.UNMETERED_APN)); boolean retValue = setupData(apnContext, radioTech); notifyOffApnsOfAvailability(apnContext.getReason()); if (DBG) log("trySetupData: X retValue=" + retValue); Loading Loading @@ -1901,11 +1870,9 @@ public class DcTracker extends Handler { * * @param apnContext APN context * @param radioTech RAT of the data connection * @param unmeteredUseOnly True if this data connection should be only used for unmetered * purposes only. * @return True if successful, otherwise false. */ private boolean setupData(ApnContext apnContext, int radioTech, boolean unmeteredUseOnly) { private boolean setupData(ApnContext apnContext, int radioTech) { if (DBG) log("setupData: apnContext=" + apnContext); apnContext.requestLog("setupData"); ApnSetting apnSetting; Loading Loading @@ -1995,7 +1962,7 @@ public class DcTracker extends Handler { Message msg = obtainMessage(); msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE; msg.obj = new Pair<ApnContext, Integer>(apnContext, generation); dataConnection.bringUp(apnContext, profileId, radioTech, unmeteredUseOnly, msg, generation); dataConnection.bringUp(apnContext, profileId, radioTech, msg, generation); if (DBG) log("setupData: initing!"); return true; Loading Loading @@ -2648,6 +2615,8 @@ public class DcTracker extends Handler { private void onDataRoamingOff() { if (DBG) log("onDataRoamingOff"); reevaluateDataConnections(); if (!getDataRoamingEnabled()) { // TODO: Remove this once all old vendor RILs are gone. We don't need to set initial apn // attach and send the data profile again as the modem should have both roaming and Loading Loading @@ -2683,6 +2652,13 @@ public class DcTracker extends Handler { checkDataRoamingStatus(settingChanged); if (getDataRoamingEnabled()) { // If the restricted data was brought up when data roaming is disabled, and now users // enable data roaming, we need to re-evaluate the conditions and possibly change the // network's capability. if (settingChanged) { reevaluateDataConnections(); } if (DBG) log("onDataRoamingOnOrSettingsChanged: setup data on roaming"); setupDataOnConnectableApns(Phone.REASON_ROAMING_ON); Loading tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java +4 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
src/java/com/android/internal/telephony/dataconnection/ApnContext.java +9 −5 Original line number Diff line number Diff line Loading @@ -460,7 +460,11 @@ public class ApnContext { } } public boolean hasNoRestrictedRequests(boolean excludeDun) { /** * @param excludeDun True if excluding requests that have DUN capability * @return True if the attached network requests contain restricted capability. */ public boolean hasRestrictedRequests(boolean excludeDun) { synchronized (mRefCountLock) { for (NetworkRequest nr : mNetworkRequests) { if (excludeDun && Loading @@ -468,13 +472,13 @@ public class ApnContext { NetworkCapabilities.NET_CAPABILITY_DUN)) { continue; } if (nr.networkCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) == false) { return false; if (!nr.networkCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) { return true; } } } return true; return false; } private final SparseIntArray mRetriesLeftPerErrorCode = new SparseIntArray(); Loading
src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java +4 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.telephony.CarrierConfigManager; import android.telephony.Rlog; import android.telephony.ServiceState; import android.telephony.data.ApnSetting; import android.text.TextUtils; import android.util.Log; import com.android.internal.telephony.Phone; Loading Loading @@ -118,7 +119,7 @@ public class ApnSettingUtils { * @return {@code true} if the APN type is metered, {@code false} otherwise. */ public static boolean isMeteredApnType(String type, Phone phone) { if (phone == null) { if (phone == null || TextUtils.isEmpty(type)) { return true; } Loading Loading @@ -193,11 +194,12 @@ public class ApnSettingUtils { /** * Check if this APN setting is metered. * * @param apn APN setting * @param phone The phone object * @return True if this APN setting is metered, otherwise false. */ public static boolean isMetered(ApnSetting apn, Phone phone) { if (phone == null) { if (phone == null || apn == null) { return true; } Loading
src/java/com/android/internal/telephony/dataconnection/DataConnection.java +136 −51 Original line number Diff line number Diff line Loading @@ -123,17 +123,14 @@ public class DataConnection extends StateMachine { ApnContext mApnContext; int mProfileId; int mRilRat; final boolean mUnmeteredUseOnly; Message mOnCompletedMsg; final int mConnectionGeneration; ConnectionParams(ApnContext apnContext, int profileId, int rilRadioTechnology, boolean unmeteredUseOnly, Message onCompletedMsg, int connectionGeneration) { Message onCompletedMsg, int connectionGeneration) { mApnContext = apnContext; mProfileId = profileId; mRilRat = rilRadioTechnology; mUnmeteredUseOnly = unmeteredUseOnly; mOnCompletedMsg = onCompletedMsg; mConnectionGeneration = connectionGeneration; } Loading @@ -143,7 +140,6 @@ public class DataConnection extends StateMachine { return "{mTag=" + mTag + " mApnContext=" + mApnContext + " mProfileId=" + mProfileId + " mRat=" + mRilRat + " mUnmeteredUseOnly=" + mUnmeteredUseOnly + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) + "}"; } } Loading Loading @@ -222,8 +218,9 @@ public class DataConnection extends StateMachine { static final int EVENT_KEEPALIVE_STOP_REQUEST = BASE + 22; static final int EVENT_LINK_CAPACITY_CHANGED = BASE + 23; static final int EVENT_RESET = BASE + 24; static final int EVENT_REEVALUATE_RESTRICTED_STATE = BASE + 25; private static final int CMD_TO_STRING_COUNT = EVENT_RESET - BASE + 1; private static final int CMD_TO_STRING_COUNT = EVENT_REEVALUATE_RESTRICTED_STATE - BASE + 1; private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT]; static { Loading Loading @@ -255,6 +252,8 @@ public class DataConnection extends StateMachine { sCmdToString[EVENT_KEEPALIVE_STOP_REQUEST - BASE] = "EVENT_KEEPALIVE_STOP_REQUEST"; sCmdToString[EVENT_LINK_CAPACITY_CHANGED - BASE] = "EVENT_LINK_CAPACITY_CHANGED"; sCmdToString[EVENT_RESET - BASE] = "EVENT_RESET"; sCmdToString[EVENT_REEVALUATE_RESTRICTED_STATE - BASE] = "EVENT_REEVALUATE_RESTRICTED_STATE"; } // Convert cmd to string or null if unknown static String cmdToString(int cmd) { Loading Loading @@ -735,6 +734,8 @@ public class DataConnection extends StateMachine { mLinkProperties = new LinkProperties(); mApnContexts.clear(); mApnSetting = null; mUnmeteredUseOnly = false; mRestrictedNetworkOverride = false; mDcFailCause = null; } Loading Loading @@ -885,14 +886,19 @@ public class DataConnection extends StateMachine { mLinkProperties.setTcpBufferSizes(sizes); } /** * Indicates if this data connection was established for unmetered use only. Note that this * flag should be populated when data becomes active. And if it is set to true, it can be set to * false later when we are reevaluating the data connection. But if it is set to false, it * can never become true later because setting it to true will cause this data connection * losing some immutable network capabilities, which can cause issues in connectivity service. */ private boolean mUnmeteredUseOnly = false; /** * Indicates if when this connection was established we had a restricted/privileged * NetworkRequest and needed it to overcome data-enabled limitations. * * This gets set once per connection setup and is based on conditions at that time. * We could theoretically have dynamic capabilities but now is not a good time to * experiment with that. * * This flag overrides the APN-based restriction capability, restricting the network * based on both having a NetworkRequest with restricted AND needing a restricted * bit to overcome user-disabled status. This allows us to handle the common case Loading @@ -900,38 +906,78 @@ public class DataConnection extends StateMachine { * if conditions require a restricted network to overcome user-disabled then it must * be restricted, otherwise it is unrestricted (or restricted based on APN type). * * Because we're not supporting dynamic capabilities, if conditions change and we go from * data-enabled to not or vice-versa we will need to tear down networks to deal with it * at connection setup time with the new state. * * This supports a privileged app bringing up a network without general apps having access * to it when the network is otherwise unavailable (hipri). The first use case is * pre-paid SIM reprovisioning over internet, where the carrier insists on no traffic * other than from the privileged carrier-app. * * Note that the data connection cannot go from unrestricted to restricted because the * connectivity service does not support dynamically closing TCP connections at this point. */ private boolean mRestrictedNetworkOverride = false; // Should be called once when the call goes active to examine the state of things and // declare the restriction override for the life of the connection private void setNetworkRestriction() { mRestrictedNetworkOverride = false; // first, if we have no restricted requests, this override can stay FALSE: boolean noRestrictedRequests = true; /** * Check if this data connection should be restricted. We should call this when data connection * becomes active, or when we want to re-evaluate the conditions to decide if we need to * unstrict the data connection. * * @return True if this data connection needs to be restricted. */ private boolean shouldRestrictNetwork() { // first, check if there is any network request that containing restricted capability // (i.e. Do not have NET_CAPABILITY_NOT_RESTRICTED in the request) boolean isAnyRestrictedRequest = false; for (ApnContext apnContext : mApnContexts.keySet()) { noRestrictedRequests &= apnContext.hasNoRestrictedRequests(true /* exclude DUN */); if (apnContext.hasRestrictedRequests(true /* exclude DUN */)) { isAnyRestrictedRequest = true; break; } if (noRestrictedRequests) { return; } // Do we need a restricted network to satisfy the request? // Is this network metered? If not, then don't add restricted // If all of the network requests are non-restricted, then we don't need to restrict // the network. if (!isAnyRestrictedRequest) { return false; } // If the network is unmetered, then we don't need to restrict the network because users // won't be charged anyway. if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) { return; return false; } // If the data is disabled, then we need to restrict the network so only privileged apps can // use the restricted network while data is disabled. if (!mDct.isDataEnabled()) { return true; } // Is data disabled? mRestrictedNetworkOverride = !mDct.isDataEnabled(); // If the device is roaming, and the user does not turn on data roaming, then we need to // restrict the network so only privileged apps can use it. if (!mDct.getDataRoamingEnabled() && mPhone.getServiceState().getDataRoaming()) { return true; } // Otherwise we should not restrict the network so anyone who requests can use it. return false; } /** * @return True if this data connection should only be used for unmetered purposes. */ private boolean isUnmeteredUseOnly() { // The data connection can only be unmetered used only if all requests' reasons are // unmetered. for (ApnContext apnContext : mApnContexts.keySet()) { DataConnectionReasons dataConnectionReasons = new DataConnectionReasons(); boolean isDataAllowed = mDct.isDataAllowed(apnContext, dataConnectionReasons); if (!isDataAllowed || !dataConnectionReasons.contains( DataConnectionReasons.DataAllowedReasonType.UNMETERED_APN)) { return false; } } return true; } NetworkCapabilities getNetworkCapabilities() { Loading @@ -942,8 +988,7 @@ public class DataConnection extends StateMachine { final String[] types = ApnSetting.getApnTypesStringFromBitmask( mApnSetting.getApnTypeBitmask()).split(","); for (String type : types) { if (!mRestrictedNetworkOverride && (mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly) if (!mRestrictedNetworkOverride && mUnmeteredUseOnly && ApnSettingUtils.isMeteredApnType(type, mPhone)) { log("Dropped the metered " + type + " for the unmetered data call."); continue; Loading Loading @@ -1003,8 +1048,7 @@ public class DataConnection extends StateMachine { // Mark NOT_METERED in the following cases, // 1. All APNs in APN settings are unmetered. // 2. The non-restricted data and is intended for unmetered use only. if (((mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly) && !mRestrictedNetworkOverride) if ((mUnmeteredUseOnly && !mRestrictedNetworkOverride) || !ApnSettingUtils.isMetered(mApnSetting, mPhone)) { result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); } else { Loading @@ -1013,6 +1057,7 @@ public class DataConnection extends StateMachine { result.maybeMarkCapabilitiesRestricted(); } if (mRestrictedNetworkOverride) { result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); // don't use dun on restriction-overriden networks. Loading Loading @@ -1270,17 +1315,11 @@ public class DataConnection extends StateMachine { break; case EVENT_DISCONNECT: if (DBG) { log("DcDefaultState deferring msg.what=EVENT_DISCONNECT RefCount=" + mApnContexts.size()); } deferMessage(msg); break; case EVENT_DISCONNECT_ALL: case EVENT_REEVALUATE_RESTRICTED_STATE: if (DBG) { log("DcDefaultState deferring msg.what=EVENT_DISCONNECT_ALL RefCount=" + mApnContexts.size()); log("DcDefaultState deferring msg.what=" + getWhatToString(msg.what) + " RefCount=" + mApnContexts.size()); } deferMessage(msg); break; Loading Loading @@ -1457,8 +1496,10 @@ public class DataConnection extends StateMachine { switch (msg.what) { case EVENT_RESET: case EVENT_REEVALUATE_RESTRICTED_STATE: if (DBG) { log("DcInactiveState: msg.what=EVENT_RESET, ignore we're already reset"); log("DcInactiveState: msg.what=" + getWhatToString(msg.what) + ", ignore we're already done"); } retVal = HANDLED; break; Loading Loading @@ -1659,8 +1700,13 @@ public class DataConnection extends StateMachine { } misc.subscriberId = mPhone.getSubscriberId(); setNetworkRestriction(); if (DBG) log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride); mRestrictedNetworkOverride = shouldRestrictNetwork(); mUnmeteredUseOnly = isUnmeteredUseOnly(); if (DBG) { log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride + ", mUnmeteredUseOnly = " + mUnmeteredUseOnly); } mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(), "DcNetworkAgent", mNetworkInfo, getNetworkCapabilities(), mLinkProperties, 50, misc); Loading Loading @@ -1924,6 +1970,37 @@ public class DataConnection extends StateMachine { retVal = HANDLED; break; } case EVENT_REEVALUATE_RESTRICTED_STATE: { // If the network was restricted, and now it does not need to be restricted // anymore, we should add the NET_CAPABILITY_NOT_RESTRICTED capability. if (mRestrictedNetworkOverride && !shouldRestrictNetwork()) { if (DBG) { log("Data connection becomes not-restricted. dc=" + this); } // Note we only do this when network becomes non-restricted. When a // non-restricted becomes restricted (e.g. users disable data, or turn off // data roaming), DCT will explicitly tear down the networks (because // connectivity service does not support force-close TCP connections today). // Also note that NET_CAPABILITY_NOT_RESTRICTED is an immutable capability // (see {@link NetworkCapabilities}) once we add it to the network, we can't // remove it through the entire life cycle of the connection. mRestrictedNetworkOverride = false; mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities()); } // If the data does need to be unmetered use only (e.g. users turn on data, or // device is not roaming anymore assuming data roaming is off), then we can // dynamically add those metered APN type capabilities back. (But not the // other way around because most of the APN-type capabilities are immutable // capabilities.) if (mUnmeteredUseOnly && !isUnmeteredUseOnly()) { mUnmeteredUseOnly = false; mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities()); } retVal = HANDLED; break; } default: if (VDBG) { log("DcActiveState not handled msg.what=" + getWhatToString(msg.what)); Loading Loading @@ -2259,7 +2336,6 @@ public class DataConnection extends StateMachine { * @param apnContext is the Access Point Name to bring up a connection to * @param profileId for the connection * @param rilRadioTechnology Radio technology for the data connection * @param unmeteredUseOnly Indicates the data connection can only used for unmetered purposes * @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(). Loading @@ -2267,15 +2343,13 @@ public class DataConnection extends StateMachine { * ignored if obsolete. */ public void bringUp(ApnContext apnContext, int profileId, int rilRadioTechnology, boolean unmeteredUseOnly, Message onCompletedMsg, int connectionGeneration) { Message onCompletedMsg, int connectionGeneration) { if (DBG) { log("bringUp: apnContext=" + apnContext + "unmeteredUseOnly=" + unmeteredUseOnly + " onCompletedMsg=" + onCompletedMsg); log("bringUp: apnContext=" + apnContext + " onCompletedMsg=" + onCompletedMsg); } sendMessage(DataConnection.EVENT_CONNECT, new ConnectionParams(apnContext, profileId, rilRadioTechnology, unmeteredUseOnly, onCompletedMsg, connectionGeneration)); new ConnectionParams(apnContext, profileId, rilRadioTechnology, onCompletedMsg, connectionGeneration)); } /** Loading Loading @@ -2324,6 +2398,15 @@ public class DataConnection extends StateMachine { if (DBG) log("reset"); } /** * Re-evaluate the restricted state. If the restricted data connection does not need to be * restricted anymore, we need to dynamically change the network's capability. */ void reevaluateRestrictedState() { sendMessage(EVENT_REEVALUATE_RESTRICTED_STATE); if (DBG) log("reevaluate restricted state"); } /** * @return The parameters used for initiating a data connection. */ Loading Loading @@ -2557,6 +2640,8 @@ public class DataConnection extends StateMachine { pw.println("mLastFailCause=" + mLastFailCause); pw.println("mUserData=" + mUserData); pw.println("mSubscriptionOverride=" + Integer.toHexString(mSubscriptionOverride)); pw.println("mRestrictedNetworkOverride=" + mRestrictedNetworkOverride); pw.println("mUnmeteredUseOnly=" + mUnmeteredUseOnly); pw.println("mInstanceNumber=" + mInstanceNumber); pw.println("mAc=" + mAc); pw.println("Network capabilities changed history:"); Loading
src/java/com/android/internal/telephony/dataconnection/DcTracker.java +22 −46 Original line number Diff line number Diff line Loading @@ -818,47 +818,16 @@ public class DcTracker extends Handler { * * For example, handle reverting restricted networks back to unrestricted. If we're changing * user data to enabled and this makes data truly enabled (not disabled by other factors) we * need to tear down any metered apn type that was enabled anyway by a privileged request. * This allows us to reconnect to it in an unrestricted way. * need to reevaluate and possibly add NET_CAPABILITY_NOT_RESTRICTED capability to the data * connection. This allows non-privilege apps to use the network. * * Or when we brought up a unmetered data connection while data is off, we only limit this * data connection for unmetered use only. When data is turned back on, we need to tear that * down so a full capable data connection can be re-established. */ private void reevaluateDataConnections() { if (mDataEnabledSettings.isDataEnabled()) { for (ApnContext apnContext : mApnContexts.values()) { if (apnContext.isConnectedOrConnecting()) { final DataConnection dataConnection = apnContext.getDataConnection(); if (dataConnection != null) { final NetworkCapabilities netCaps = dataConnection.getNetworkCapabilities(); if (netCaps != null && !netCaps.hasCapability(NetworkCapabilities .NET_CAPABILITY_NOT_RESTRICTED) && !netCaps.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) { if (DBG) { log("Tearing down restricted metered net:" + apnContext); } // Tearing down the restricted metered data call when // conditions change. This will allow reestablishing a new unrestricted // data connection. apnContext.setReason(Phone.REASON_DATA_ENABLED); cleanUpConnection(true, apnContext); } else if (ApnSettingUtils.isMetered(apnContext.getApnSetting(), mPhone) && (netCaps != null && netCaps.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_METERED))) { if (DBG) { log("Tearing down unmetered net:" + apnContext); } // The APN settings is metered, but the data was still marked as // unmetered data, must be the unmetered data connection brought up when // data is off. We need to tear that down when data is enabled again. // This will allow reestablishing a new full capability data connection. apnContext.setReason(Phone.REASON_DATA_ENABLED); cleanUpConnection(true, apnContext); } } } } for (DataConnection dataConnection : mDataConnections.values()) { dataConnection.reevaluateRestrictedState(); } } Loading Loading @@ -1244,7 +1213,8 @@ public class DcTracker extends Handler { * provided. * @return True if data connection is allowed, otherwise false. */ boolean isDataAllowed(ApnContext apnContext, DataConnectionReasons dataConnectionReasons) { public boolean isDataAllowed(ApnContext apnContext, DataConnectionReasons dataConnectionReasons) { // Step 1: Get all environment conditions. // Step 2: Special handling for emergency APN. // Step 3. Build disallowed reasons. Loading Loading @@ -1367,11 +1337,11 @@ public class DcTracker extends Handler { reasons.add(DataAllowedReasonType.UNMETERED_APN); } // If the request is restricted and there are only disallowed reasons due to data // disabled, we should allow the data. // If the request is restricted and there are only soft disallowed reasons (e.g. data // disabled, data roaming disabled) existing, we should allow the data. if (apnContext != null && !apnContext.hasNoRestrictedRequests(true) && reasons.contains(DataDisallowedReasonType.DATA_DISABLED)) { && apnContext.hasRestrictedRequests(true) && !reasons.allowed()) { reasons.add(DataAllowedReasonType.RESTRICTED_REQUEST); } Loading Loading @@ -1491,8 +1461,7 @@ public class DcTracker extends Handler { } } boolean retValue = setupData(apnContext, radioTech, dataConnectionReasons.contains( DataAllowedReasonType.UNMETERED_APN)); boolean retValue = setupData(apnContext, radioTech); notifyOffApnsOfAvailability(apnContext.getReason()); if (DBG) log("trySetupData: X retValue=" + retValue); Loading Loading @@ -1901,11 +1870,9 @@ public class DcTracker extends Handler { * * @param apnContext APN context * @param radioTech RAT of the data connection * @param unmeteredUseOnly True if this data connection should be only used for unmetered * purposes only. * @return True if successful, otherwise false. */ private boolean setupData(ApnContext apnContext, int radioTech, boolean unmeteredUseOnly) { private boolean setupData(ApnContext apnContext, int radioTech) { if (DBG) log("setupData: apnContext=" + apnContext); apnContext.requestLog("setupData"); ApnSetting apnSetting; Loading Loading @@ -1995,7 +1962,7 @@ public class DcTracker extends Handler { Message msg = obtainMessage(); msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE; msg.obj = new Pair<ApnContext, Integer>(apnContext, generation); dataConnection.bringUp(apnContext, profileId, radioTech, unmeteredUseOnly, msg, generation); dataConnection.bringUp(apnContext, profileId, radioTech, msg, generation); if (DBG) log("setupData: initing!"); return true; Loading Loading @@ -2648,6 +2615,8 @@ public class DcTracker extends Handler { private void onDataRoamingOff() { if (DBG) log("onDataRoamingOff"); reevaluateDataConnections(); if (!getDataRoamingEnabled()) { // TODO: Remove this once all old vendor RILs are gone. We don't need to set initial apn // attach and send the data profile again as the modem should have both roaming and Loading Loading @@ -2683,6 +2652,13 @@ public class DcTracker extends Handler { checkDataRoamingStatus(settingChanged); if (getDataRoamingEnabled()) { // If the restricted data was brought up when data roaming is disabled, and now users // enable data roaming, we need to re-evaluate the conditions and possibly change the // network's capability. if (settingChanged) { reevaluateDataConnections(); } if (DBG) log("onDataRoamingOnOrSettingsChanged: setup data on roaming"); setupDataOnConnectableApns(Phone.REASON_ROAMING_ON); Loading
tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java +4 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes