Loading src/java/com/android/internal/telephony/SubscriptionController.java +14 −14 Original line number Diff line number Diff line Loading @@ -76,7 +76,6 @@ import android.util.Log; import com.android.ims.ImsManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.IccCardConstants.State; import com.android.internal.telephony.data.DataEnabledOverride; import com.android.internal.telephony.data.PhoneSwitcher; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.uicc.IccUtils; Loading Loading @@ -310,7 +309,7 @@ public class SubscriptionController extends ISub.Stub { SubscriptionManager.WFC_IMS_ROAMING_ENABLED, SubscriptionManager.DATA_ROAMING, SubscriptionManager.DISPLAY_NAME, SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES, SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES, SubscriptionManager.UICC_APPLICATIONS_ENABLED, SubscriptionManager.IMS_RCS_UCE_ENABLED, SubscriptionManager.CROSS_SIM_CALLING_ENABLED, Loading Loading @@ -2342,7 +2341,7 @@ public class SubscriptionController extends ISub.Stub { values.put(propKey, cursor.getInt(columnIndex)); break; case SubscriptionManager.DISPLAY_NAME: case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES: case SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES: values.put(propKey, cursor.getString(columnIndex)); break; default: Loading Loading @@ -3379,7 +3378,7 @@ public class SubscriptionController extends ISub.Stub { case SubscriptionManager.CROSS_SIM_CALLING_ENABLED: case SubscriptionManager.IS_OPPORTUNISTIC: case SubscriptionManager.GROUP_UUID: case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES: case SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES: case SubscriptionManager.ALLOWED_NETWORK_TYPES: case SubscriptionManager.D2D_STATUS_SHARING: case SubscriptionManager.VOIMS_OPT_IN_STATUS: Loading Loading @@ -4564,19 +4563,19 @@ public class SubscriptionController extends ISub.Stub { } /** * Set allowing mobile data during voice call. * Set enabled mobile data policies. * * @param subId Subscription index * @param rules Data enabled override rules in string format. See {@link DataEnabledOverride} * for details. * @param policies Mobile data policies in string format. * See {@link TelephonyManager.MobileDataPolicy} for details. * @return {@code true} if settings changed, otherwise {@code false}. */ public boolean setDataEnabledOverrideRules(int subId, @NonNull String rules) { if (DBG) logd("[setDataEnabledOverrideRules]+ rules:" + rules + " subId:" + subId); public boolean setEnabledMobileDataPolicies(int subId, @NonNull String policies) { if (DBG) logd("[setEnabledMobileDataPolicies]+ policies:" + policies + " subId:" + subId); validateSubId(subId); ContentValues value = new ContentValues(1); value.put(SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES, rules); value.put(SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES, policies); boolean result = updateDatabase(value, subId, true) > 0; Loading @@ -4590,15 +4589,16 @@ public class SubscriptionController extends ISub.Stub { } /** * Get data enabled override rules. * Get enabled mobile data policies. * * @param subId Subscription index * @return Data enabled override rules in string * @return Enabled mobile data policies joined by "," (ie. "1,2") or an empty string if no * policies are enabled. */ @NonNull public String getDataEnabledOverrideRules(int subId) { public String getEnabledMobileDataPolicies(int subId) { return TelephonyUtils.emptyIfNull(getSubscriptionProperty(subId, SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES)); SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES)); } /** Loading src/java/com/android/internal/telephony/data/DataEnabledOverride.javadeleted 100644 → 0 +0 −460 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.data; import android.annotation.IntDef; import android.annotation.NonNull; import android.telephony.Annotation.ApnType; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.SubscriptionController; import com.android.internal.telephony.data.DataEnabledOverride.OverrideConditions.Condition; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; /** * This class represents the rules for overriding data enabled settings in different conditions. * When data is disabled by the user, data can still be turned on temporarily when conditions * satisfy any rule here. */ public class DataEnabledOverride { private final Set<OverrideRule> mRules = new HashSet<>(); /** * The rule for allowing data during voice call. */ private static final OverrideRule OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL = new OverrideRule(ApnSetting.TYPE_ALL, OverrideConditions.CONDITION_IN_VOICE_CALL | OverrideConditions.CONDITION_NON_DEFAULT | OverrideConditions.CONDITION_DEFAULT_DATA_ENABLED | OverrideConditions.CONDITION_DSDS_ENABLED); /** * The rule for always allowing mms. Without adding any condition to the rule, any condition can * satisfy this rule for mms. */ private static final OverrideRule OVERRIDE_RULE_ALWAYS_ALLOW_MMS = new OverrideRule(ApnSetting.TYPE_MMS, OverrideConditions.CONDITION_UNCONDITIONALLY); /** * Data enabled override rule */ private static class OverrideRule { /** * APN type of the rule. The rule is APN type specific. The override is applicable to the * specified APN type as well. For now we only support one APN type per rule. Can be * expanded to multiple APN types in the future. */ private final @ApnType int mApnType; /** The required conditions for overriding */ private final OverrideConditions mRequiredConditions; /** * Constructor * * @param rule The override rule string. For example, {@code mms=nonDefault} or * {@code default=voiceCall & nonDefault} */ OverrideRule(@NonNull String rule) { String[] tokens = rule.trim().split("\\s*=\\s*"); if (tokens.length != 2) { throw new IllegalArgumentException("Invalid data enabled override rule format: " + rule); } if (TextUtils.isEmpty(tokens[0])) { throw new IllegalArgumentException("APN type can't be empty"); } mApnType = ApnSetting.getApnTypesBitmaskFromString(tokens[0]); if (mApnType == ApnSetting.TYPE_NONE) { throw new IllegalArgumentException("Invalid APN type. Rule=" + rule); } mRequiredConditions = new OverrideConditions(tokens[1]); } /** * Constructor * * @param apnType APN type of the rule * @param requiredConditions The required conditions for the rule */ private OverrideRule(int apnType, int requiredConditions) { mApnType = apnType; mRequiredConditions = new OverrideConditions(requiredConditions); } /** * Check if this rule can be satisfied by the given APN type and provided conditions. * * @param apnType APN type to check * @param providedConditions The provided conditions to check * @return {@code true} if satisfied */ boolean isSatisfiedByConditions(@ApnType int apnType, @Condition int providedConditions) { return (mApnType == apnType || mApnType == ApnSetting.TYPE_ALL) && mRequiredConditions.allMet(providedConditions); } @Override public String toString() { return ApnSetting.getApnTypeString(mApnType) + "=" + mRequiredConditions; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; OverrideRule that = (OverrideRule) o; return mApnType == that.mApnType && Objects.equals(mRequiredConditions, that.mRequiredConditions); } @Override public int hashCode() { return Objects.hash(mApnType, mRequiredConditions); } } /** * Represent the conditions for overriding data enabled settings */ static class OverrideConditions { // Possible values for data enabled override condition. Note these flags are bitmasks. /** Unconditionally override enabled settings */ static final int CONDITION_UNCONDITIONALLY = 0; /** Enable data only on subscription that is not user selected default data subscription */ static final int CONDITION_NON_DEFAULT = 1 << 0; /** Enable data only when device has ongoing voice call */ static final int CONDITION_IN_VOICE_CALL = 1 << 1; /** Enable data only when default data is on */ static final int CONDITION_DEFAULT_DATA_ENABLED = 1 << 2; /** Enable data only when device is in DSDS mode */ static final int CONDITION_DSDS_ENABLED = 1 << 3; /** Enable data unconditionally in string format */ static final String CONDITION_UNCONDITIONALLY_STRING = "unconditionally"; /** Enable data only on subscription that is not default in string format */ static final String CONDITION_NON_DEFAULT_STRING = "nonDefault"; /** Enable data only when device has ongoing voice call in string format */ static final String CONDITION_VOICE_CALL_STRING = "inVoiceCall"; /** Enable data only when default data is on in string format */ static final String CONDITION_DEFAULT_DATA_ENABLED_STRING = "DefaultDataOn"; /** Enable data only when device is in DSDS mode in string format */ static final String CONDITION_DSDS_ENABLED_STRING = "dsdsEnabled"; /** @hide */ @IntDef(flag = true, prefix = { "OVERRIDE_CONDITION_" }, value = { CONDITION_NON_DEFAULT, CONDITION_IN_VOICE_CALL, CONDITION_DEFAULT_DATA_ENABLED, CONDITION_DSDS_ENABLED }) @Retention(RetentionPolicy.SOURCE) public @interface Condition {} private static final Map<Integer, String> OVERRIDE_CONDITION_INT_MAP = new ArrayMap<>(); private static final Map<String, Integer> OVERRIDE_CONDITION_STRING_MAP = new ArrayMap<>(); static { OVERRIDE_CONDITION_INT_MAP.put(CONDITION_NON_DEFAULT, CONDITION_NON_DEFAULT_STRING); OVERRIDE_CONDITION_INT_MAP.put(CONDITION_IN_VOICE_CALL, CONDITION_VOICE_CALL_STRING); OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DEFAULT_DATA_ENABLED, CONDITION_DEFAULT_DATA_ENABLED_STRING); OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DSDS_ENABLED, CONDITION_DSDS_ENABLED_STRING); OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_UNCONDITIONALLY_STRING, CONDITION_UNCONDITIONALLY); OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_NON_DEFAULT_STRING, CONDITION_NON_DEFAULT); OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_VOICE_CALL_STRING, CONDITION_IN_VOICE_CALL); OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DEFAULT_DATA_ENABLED_STRING, CONDITION_DEFAULT_DATA_ENABLED); OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DSDS_ENABLED_STRING, CONDITION_DSDS_ENABLED); } private final @Condition int mConditions; /** * Conditions for overriding data enabled setting * * @param conditions Conditions in string format */ OverrideConditions(@NonNull String conditions) { mConditions = getBitmaskFromString(conditions); } /** * Conditions for overriding data enabled setting * * @param conditions Conditions in bitmask */ OverrideConditions(@Condition int conditions) { mConditions = conditions; } private static String getStringFromBitmask(@Condition int conditions) { if (conditions == CONDITION_UNCONDITIONALLY) { return CONDITION_UNCONDITIONALLY_STRING; } List<String> conditionsStrings = new ArrayList<>(); for (Integer condition : OVERRIDE_CONDITION_INT_MAP.keySet()) { if ((conditions & condition) == condition) { conditionsStrings.add(OVERRIDE_CONDITION_INT_MAP.get(condition)); } } return TextUtils.join("&", conditionsStrings); } private static @Condition int getBitmaskFromString(@NonNull String str) { if (TextUtils.isEmpty(str)) { throw new IllegalArgumentException("Empty rule string"); } String[] conditionStrings = str.trim().split("\\s*&\\s*"); int bitmask = 0; for (String conditionStr : conditionStrings) { if (!TextUtils.isEmpty(conditionStr)) { if (!OVERRIDE_CONDITION_STRING_MAP.containsKey(conditionStr)) { throw new IllegalArgumentException("Invalid conditions: " + str); } bitmask |= OVERRIDE_CONDITION_STRING_MAP.get(conditionStr); } } return bitmask; } /** * Check if provided conditions can meet all conditions in the rule. * * @param providedConditions The provided conditions * @return {@code true} if all conditions are met. */ boolean allMet(@Condition int providedConditions) { return (providedConditions & mConditions) == mConditions; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; OverrideConditions that = (OverrideConditions) o; return mConditions == that.mConditions; } @Override public int hashCode() { return Objects.hash(mConditions); } @Override public String toString() { return getStringFromBitmask(mConditions); } } /** * Constructor * * @param rules Data enabled override rules */ public DataEnabledOverride(@NonNull String rules) { updateRules(rules); } /** * Update the data enabled override rules. * * @param newRules New override rules */ @VisibleForTesting public void updateRules(@NonNull String newRules) { mRules.clear(); String[] rulesString = newRules.trim().split("\\s*,\\s*"); for (String rule : rulesString) { if (!TextUtils.isEmpty(rule)) { mRules.add(new OverrideRule(rule)); } } } /** * Set always allowing MMS * * @param allow {@code true} if always allowing, otherwise {@code false}. */ public void setAlwaysAllowMms(boolean allow) { if (allow) { mRules.add(OVERRIDE_RULE_ALWAYS_ALLOW_MMS); } else { mRules.remove(OVERRIDE_RULE_ALWAYS_ALLOW_MMS); } } /** * Set allowing mobile data during voice call. This is used for allowing data on the non-default * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will * not be able to use mobile data. By calling this API, data will be temporarily enabled on the * non-default data SIM during the life cycle of the voice call. * * @param allow {@code true} if allowing using data during voice call, {@code false} if * disallowed. */ public void setDataAllowedInVoiceCall(boolean allow) { if (allow) { mRules.add(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL); } else { mRules.remove(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL); } } /** * Check if data is allowed during voice call. * * @return {@code true} if data is allowed during voice call. */ public boolean isDataAllowedInVoiceCall() { return mRules.contains(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL); } public boolean isMmsAlwaysAllowed() { return mRules.contains(OVERRIDE_RULE_ALWAYS_ALLOW_MMS); } private boolean canSatisfyAnyRule(@ApnType int apnType, @Condition int providedConditions) { for (OverrideRule rule : mRules) { if (rule.isSatisfiedByConditions(apnType, providedConditions)) { return true; } } return false; } private @Condition int getCurrentConditions(Phone phone) { int conditions = 0; if (phone != null) { // Check if the device is on voice call if (phone.getState() != PhoneConstants.State.IDLE) { conditions |= OverrideConditions.CONDITION_IN_VOICE_CALL; } int defaultDataSubId = SubscriptionController.getInstance().getDefaultDataSubId(); if (phone.getSubId() != defaultDataSubId) { conditions |= OverrideConditions.CONDITION_NON_DEFAULT; } if (defaultDataSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { int phoneId = SubscriptionController.getInstance().getPhoneId(defaultDataSubId); try { Phone defaultDataPhone = PhoneFactory.getPhone(phoneId); if (defaultDataPhone != null && defaultDataPhone.isUserDataEnabled()) { conditions |= OverrideConditions.CONDITION_DEFAULT_DATA_ENABLED; } } catch (IllegalStateException e) { //ignore the exception and do not add the condition Log.d("DataEnabledOverride", e.getMessage()); } } if (TelephonyManager.from(phone.getContext()).isMultiSimEnabled()) { conditions |= OverrideConditions.CONDITION_DSDS_ENABLED; } } return conditions; } /** * Check for given APN type if we should enable data. * * @param phone Phone object * @param apnType APN type * @return {@code true} if data should be enabled for the current condition. */ public boolean shouldOverrideDataEnabledSettings(Phone phone, @ApnType int apnType) { return canSatisfyAnyRule(apnType, getCurrentConditions(phone)); } /** * Get data enabled override rules. * * @return Get data enabled override rules in string format */ @NonNull public String getRules() { List<String> ruleStrings = new ArrayList<>(); for (OverrideRule rule : mRules) { ruleStrings.add(rule.toString()); } return TextUtils.join(",", ruleStrings); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DataEnabledOverride that = (DataEnabledOverride) o; return mRules.equals(that.mRules); } @Override public int hashCode() { return Objects.hash(mRules); } @Override public String toString() { return "DataEnabledOverride: [rules=\"" + getRules() + "\"]"; } } src/java/com/android/internal/telephony/data/DataNetworkController.java +2 −1 Original line number Diff line number Diff line Loading @@ -1567,7 +1567,8 @@ public class DataNetworkController extends Handler { if (!mDataSettingsManager.isDataEnabled() && networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS) && mDataSettingsManager.isMmsAlwaysAllowed()) { && mDataSettingsManager.isMobileDataPolicyEnabled(TelephonyManager .MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED)) { // We reach here when data is disabled, but MMS always-allowed is enabled. // (Note that isDataEnabled(ApnSetting.TYPE_MMS) returns true in this case, so it // would not generate any soft disallowed reason. We need to explicitly handle it.) Loading src/java/com/android/internal/telephony/data/DataSettingsManager.java +126 −65 File changed.Preview size limit exceeded, changes collapsed. Show changes src/java/com/android/internal/telephony/data/DataUtils.java +36 −3 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.telephony.Annotation.NetCapability; import android.telephony.Annotation.NetworkType; import android.telephony.Annotation.ValidationStatus; import android.telephony.TelephonyManager; import android.telephony.TelephonyManager.MobileDataPolicy; import android.telephony.data.ApnSetting; import android.telephony.data.ApnSetting.ApnType; import android.telephony.data.DataCallResponse; Loading @@ -41,6 +42,7 @@ import android.telephony.ims.feature.ImsFeature; import android.util.ArrayMap; import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList; import com.android.telephony.Rlog; import java.text.SimpleDateFormat; import java.util.ArrayList; Loading @@ -61,6 +63,7 @@ public class DataUtils { /** The time format for converting time to readable string. */ private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS", Locale.US); private static final String TAG = "DataUtils"; /** * Get the network capability from the string. Loading Loading @@ -164,6 +167,7 @@ public class DataUtils { case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH: return "PRIORITIZE_BANDWIDTH"; default: loge("Unknown network capability(" + netCap + ")"); return "Unknown(" + netCap + ")"; } } Loading Loading @@ -209,7 +213,9 @@ public class DataUtils { switch (status) { case NetworkAgent.VALIDATION_STATUS_VALID: return "VALID"; case NetworkAgent.VALIDATION_STATUS_NOT_VALID: return "INVALID"; default: return "UNKNOWN(" + status + ")"; default: loge("Unknown validation status(" + status + ")"); return "UNKNOWN(" + status + ")"; } } Loading Loading @@ -366,6 +372,7 @@ public class DataUtils { case ImsFeature.FEATURE_MMTEL: return "MMTEL"; case ImsFeature.FEATURE_RCS: return "RCS"; default: loge("Unknown IMS feature(" + imsFeature + ")"); return "Unknown(" + imsFeature + ")"; } } Loading Loading @@ -468,7 +475,9 @@ public class DataUtils { case DataCallResponse.LINK_STATUS_INACTIVE: return "INACTIVE"; case DataCallResponse.LINK_STATUS_ACTIVE: return "ACTIVE"; case DataCallResponse.LINK_STATUS_DORMANT: return "DORMANT"; default: return "UNKNOWN(" + linkStatus + ")"; default: loge("Unknown link status(" + linkStatus + ")"); return "UNKNOWN(" + linkStatus + ")"; } } Loading Loading @@ -505,7 +514,31 @@ public class DataUtils { case TelephonyManager.DATA_ACTIVITY_OUT: return "OUT"; case TelephonyManager.DATA_ACTIVITY_INOUT: return "INOUT"; case TelephonyManager.DATA_ACTIVITY_DORMANT: return "DORMANT"; default: return "UNKNOWN(" + dataActivity + ")"; default: loge("Unknown data activity(" + dataActivity + ")"); return "UNKNOWN(" + dataActivity + ")"; } } /** * Convert mobile data policy to string. * * @param mobileDataPolicy The mobile data policy. * @return The mobile data policy in string format. */ public static @NonNull String mobileDataPolicyToString(@MobileDataPolicy int mobileDataPolicy) { switch (mobileDataPolicy) { case TelephonyManager.MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL: return "DATA_ON_NON_DEFAULT_DURING_VOICE_CALL"; case TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED: return "MMS_ALWAYS_ALLOWED"; default: loge("Unknown mobile data policy(" + mobileDataPolicy + ")"); return "UNKNOWN(" + mobileDataPolicy + ")"; } } private static void loge(String msg) { Rlog.e(TAG, msg); } } Loading
src/java/com/android/internal/telephony/SubscriptionController.java +14 −14 Original line number Diff line number Diff line Loading @@ -76,7 +76,6 @@ import android.util.Log; import com.android.ims.ImsManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.IccCardConstants.State; import com.android.internal.telephony.data.DataEnabledOverride; import com.android.internal.telephony.data.PhoneSwitcher; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.uicc.IccUtils; Loading Loading @@ -310,7 +309,7 @@ public class SubscriptionController extends ISub.Stub { SubscriptionManager.WFC_IMS_ROAMING_ENABLED, SubscriptionManager.DATA_ROAMING, SubscriptionManager.DISPLAY_NAME, SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES, SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES, SubscriptionManager.UICC_APPLICATIONS_ENABLED, SubscriptionManager.IMS_RCS_UCE_ENABLED, SubscriptionManager.CROSS_SIM_CALLING_ENABLED, Loading Loading @@ -2342,7 +2341,7 @@ public class SubscriptionController extends ISub.Stub { values.put(propKey, cursor.getInt(columnIndex)); break; case SubscriptionManager.DISPLAY_NAME: case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES: case SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES: values.put(propKey, cursor.getString(columnIndex)); break; default: Loading Loading @@ -3379,7 +3378,7 @@ public class SubscriptionController extends ISub.Stub { case SubscriptionManager.CROSS_SIM_CALLING_ENABLED: case SubscriptionManager.IS_OPPORTUNISTIC: case SubscriptionManager.GROUP_UUID: case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES: case SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES: case SubscriptionManager.ALLOWED_NETWORK_TYPES: case SubscriptionManager.D2D_STATUS_SHARING: case SubscriptionManager.VOIMS_OPT_IN_STATUS: Loading Loading @@ -4564,19 +4563,19 @@ public class SubscriptionController extends ISub.Stub { } /** * Set allowing mobile data during voice call. * Set enabled mobile data policies. * * @param subId Subscription index * @param rules Data enabled override rules in string format. See {@link DataEnabledOverride} * for details. * @param policies Mobile data policies in string format. * See {@link TelephonyManager.MobileDataPolicy} for details. * @return {@code true} if settings changed, otherwise {@code false}. */ public boolean setDataEnabledOverrideRules(int subId, @NonNull String rules) { if (DBG) logd("[setDataEnabledOverrideRules]+ rules:" + rules + " subId:" + subId); public boolean setEnabledMobileDataPolicies(int subId, @NonNull String policies) { if (DBG) logd("[setEnabledMobileDataPolicies]+ policies:" + policies + " subId:" + subId); validateSubId(subId); ContentValues value = new ContentValues(1); value.put(SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES, rules); value.put(SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES, policies); boolean result = updateDatabase(value, subId, true) > 0; Loading @@ -4590,15 +4589,16 @@ public class SubscriptionController extends ISub.Stub { } /** * Get data enabled override rules. * Get enabled mobile data policies. * * @param subId Subscription index * @return Data enabled override rules in string * @return Enabled mobile data policies joined by "," (ie. "1,2") or an empty string if no * policies are enabled. */ @NonNull public String getDataEnabledOverrideRules(int subId) { public String getEnabledMobileDataPolicies(int subId) { return TelephonyUtils.emptyIfNull(getSubscriptionProperty(subId, SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES)); SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES)); } /** Loading
src/java/com/android/internal/telephony/data/DataEnabledOverride.javadeleted 100644 → 0 +0 −460 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.data; import android.annotation.IntDef; import android.annotation.NonNull; import android.telephony.Annotation.ApnType; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.SubscriptionController; import com.android.internal.telephony.data.DataEnabledOverride.OverrideConditions.Condition; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; /** * This class represents the rules for overriding data enabled settings in different conditions. * When data is disabled by the user, data can still be turned on temporarily when conditions * satisfy any rule here. */ public class DataEnabledOverride { private final Set<OverrideRule> mRules = new HashSet<>(); /** * The rule for allowing data during voice call. */ private static final OverrideRule OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL = new OverrideRule(ApnSetting.TYPE_ALL, OverrideConditions.CONDITION_IN_VOICE_CALL | OverrideConditions.CONDITION_NON_DEFAULT | OverrideConditions.CONDITION_DEFAULT_DATA_ENABLED | OverrideConditions.CONDITION_DSDS_ENABLED); /** * The rule for always allowing mms. Without adding any condition to the rule, any condition can * satisfy this rule for mms. */ private static final OverrideRule OVERRIDE_RULE_ALWAYS_ALLOW_MMS = new OverrideRule(ApnSetting.TYPE_MMS, OverrideConditions.CONDITION_UNCONDITIONALLY); /** * Data enabled override rule */ private static class OverrideRule { /** * APN type of the rule. The rule is APN type specific. The override is applicable to the * specified APN type as well. For now we only support one APN type per rule. Can be * expanded to multiple APN types in the future. */ private final @ApnType int mApnType; /** The required conditions for overriding */ private final OverrideConditions mRequiredConditions; /** * Constructor * * @param rule The override rule string. For example, {@code mms=nonDefault} or * {@code default=voiceCall & nonDefault} */ OverrideRule(@NonNull String rule) { String[] tokens = rule.trim().split("\\s*=\\s*"); if (tokens.length != 2) { throw new IllegalArgumentException("Invalid data enabled override rule format: " + rule); } if (TextUtils.isEmpty(tokens[0])) { throw new IllegalArgumentException("APN type can't be empty"); } mApnType = ApnSetting.getApnTypesBitmaskFromString(tokens[0]); if (mApnType == ApnSetting.TYPE_NONE) { throw new IllegalArgumentException("Invalid APN type. Rule=" + rule); } mRequiredConditions = new OverrideConditions(tokens[1]); } /** * Constructor * * @param apnType APN type of the rule * @param requiredConditions The required conditions for the rule */ private OverrideRule(int apnType, int requiredConditions) { mApnType = apnType; mRequiredConditions = new OverrideConditions(requiredConditions); } /** * Check if this rule can be satisfied by the given APN type and provided conditions. * * @param apnType APN type to check * @param providedConditions The provided conditions to check * @return {@code true} if satisfied */ boolean isSatisfiedByConditions(@ApnType int apnType, @Condition int providedConditions) { return (mApnType == apnType || mApnType == ApnSetting.TYPE_ALL) && mRequiredConditions.allMet(providedConditions); } @Override public String toString() { return ApnSetting.getApnTypeString(mApnType) + "=" + mRequiredConditions; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; OverrideRule that = (OverrideRule) o; return mApnType == that.mApnType && Objects.equals(mRequiredConditions, that.mRequiredConditions); } @Override public int hashCode() { return Objects.hash(mApnType, mRequiredConditions); } } /** * Represent the conditions for overriding data enabled settings */ static class OverrideConditions { // Possible values for data enabled override condition. Note these flags are bitmasks. /** Unconditionally override enabled settings */ static final int CONDITION_UNCONDITIONALLY = 0; /** Enable data only on subscription that is not user selected default data subscription */ static final int CONDITION_NON_DEFAULT = 1 << 0; /** Enable data only when device has ongoing voice call */ static final int CONDITION_IN_VOICE_CALL = 1 << 1; /** Enable data only when default data is on */ static final int CONDITION_DEFAULT_DATA_ENABLED = 1 << 2; /** Enable data only when device is in DSDS mode */ static final int CONDITION_DSDS_ENABLED = 1 << 3; /** Enable data unconditionally in string format */ static final String CONDITION_UNCONDITIONALLY_STRING = "unconditionally"; /** Enable data only on subscription that is not default in string format */ static final String CONDITION_NON_DEFAULT_STRING = "nonDefault"; /** Enable data only when device has ongoing voice call in string format */ static final String CONDITION_VOICE_CALL_STRING = "inVoiceCall"; /** Enable data only when default data is on in string format */ static final String CONDITION_DEFAULT_DATA_ENABLED_STRING = "DefaultDataOn"; /** Enable data only when device is in DSDS mode in string format */ static final String CONDITION_DSDS_ENABLED_STRING = "dsdsEnabled"; /** @hide */ @IntDef(flag = true, prefix = { "OVERRIDE_CONDITION_" }, value = { CONDITION_NON_DEFAULT, CONDITION_IN_VOICE_CALL, CONDITION_DEFAULT_DATA_ENABLED, CONDITION_DSDS_ENABLED }) @Retention(RetentionPolicy.SOURCE) public @interface Condition {} private static final Map<Integer, String> OVERRIDE_CONDITION_INT_MAP = new ArrayMap<>(); private static final Map<String, Integer> OVERRIDE_CONDITION_STRING_MAP = new ArrayMap<>(); static { OVERRIDE_CONDITION_INT_MAP.put(CONDITION_NON_DEFAULT, CONDITION_NON_DEFAULT_STRING); OVERRIDE_CONDITION_INT_MAP.put(CONDITION_IN_VOICE_CALL, CONDITION_VOICE_CALL_STRING); OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DEFAULT_DATA_ENABLED, CONDITION_DEFAULT_DATA_ENABLED_STRING); OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DSDS_ENABLED, CONDITION_DSDS_ENABLED_STRING); OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_UNCONDITIONALLY_STRING, CONDITION_UNCONDITIONALLY); OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_NON_DEFAULT_STRING, CONDITION_NON_DEFAULT); OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_VOICE_CALL_STRING, CONDITION_IN_VOICE_CALL); OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DEFAULT_DATA_ENABLED_STRING, CONDITION_DEFAULT_DATA_ENABLED); OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DSDS_ENABLED_STRING, CONDITION_DSDS_ENABLED); } private final @Condition int mConditions; /** * Conditions for overriding data enabled setting * * @param conditions Conditions in string format */ OverrideConditions(@NonNull String conditions) { mConditions = getBitmaskFromString(conditions); } /** * Conditions for overriding data enabled setting * * @param conditions Conditions in bitmask */ OverrideConditions(@Condition int conditions) { mConditions = conditions; } private static String getStringFromBitmask(@Condition int conditions) { if (conditions == CONDITION_UNCONDITIONALLY) { return CONDITION_UNCONDITIONALLY_STRING; } List<String> conditionsStrings = new ArrayList<>(); for (Integer condition : OVERRIDE_CONDITION_INT_MAP.keySet()) { if ((conditions & condition) == condition) { conditionsStrings.add(OVERRIDE_CONDITION_INT_MAP.get(condition)); } } return TextUtils.join("&", conditionsStrings); } private static @Condition int getBitmaskFromString(@NonNull String str) { if (TextUtils.isEmpty(str)) { throw new IllegalArgumentException("Empty rule string"); } String[] conditionStrings = str.trim().split("\\s*&\\s*"); int bitmask = 0; for (String conditionStr : conditionStrings) { if (!TextUtils.isEmpty(conditionStr)) { if (!OVERRIDE_CONDITION_STRING_MAP.containsKey(conditionStr)) { throw new IllegalArgumentException("Invalid conditions: " + str); } bitmask |= OVERRIDE_CONDITION_STRING_MAP.get(conditionStr); } } return bitmask; } /** * Check if provided conditions can meet all conditions in the rule. * * @param providedConditions The provided conditions * @return {@code true} if all conditions are met. */ boolean allMet(@Condition int providedConditions) { return (providedConditions & mConditions) == mConditions; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; OverrideConditions that = (OverrideConditions) o; return mConditions == that.mConditions; } @Override public int hashCode() { return Objects.hash(mConditions); } @Override public String toString() { return getStringFromBitmask(mConditions); } } /** * Constructor * * @param rules Data enabled override rules */ public DataEnabledOverride(@NonNull String rules) { updateRules(rules); } /** * Update the data enabled override rules. * * @param newRules New override rules */ @VisibleForTesting public void updateRules(@NonNull String newRules) { mRules.clear(); String[] rulesString = newRules.trim().split("\\s*,\\s*"); for (String rule : rulesString) { if (!TextUtils.isEmpty(rule)) { mRules.add(new OverrideRule(rule)); } } } /** * Set always allowing MMS * * @param allow {@code true} if always allowing, otherwise {@code false}. */ public void setAlwaysAllowMms(boolean allow) { if (allow) { mRules.add(OVERRIDE_RULE_ALWAYS_ALLOW_MMS); } else { mRules.remove(OVERRIDE_RULE_ALWAYS_ALLOW_MMS); } } /** * Set allowing mobile data during voice call. This is used for allowing data on the non-default * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will * not be able to use mobile data. By calling this API, data will be temporarily enabled on the * non-default data SIM during the life cycle of the voice call. * * @param allow {@code true} if allowing using data during voice call, {@code false} if * disallowed. */ public void setDataAllowedInVoiceCall(boolean allow) { if (allow) { mRules.add(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL); } else { mRules.remove(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL); } } /** * Check if data is allowed during voice call. * * @return {@code true} if data is allowed during voice call. */ public boolean isDataAllowedInVoiceCall() { return mRules.contains(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL); } public boolean isMmsAlwaysAllowed() { return mRules.contains(OVERRIDE_RULE_ALWAYS_ALLOW_MMS); } private boolean canSatisfyAnyRule(@ApnType int apnType, @Condition int providedConditions) { for (OverrideRule rule : mRules) { if (rule.isSatisfiedByConditions(apnType, providedConditions)) { return true; } } return false; } private @Condition int getCurrentConditions(Phone phone) { int conditions = 0; if (phone != null) { // Check if the device is on voice call if (phone.getState() != PhoneConstants.State.IDLE) { conditions |= OverrideConditions.CONDITION_IN_VOICE_CALL; } int defaultDataSubId = SubscriptionController.getInstance().getDefaultDataSubId(); if (phone.getSubId() != defaultDataSubId) { conditions |= OverrideConditions.CONDITION_NON_DEFAULT; } if (defaultDataSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { int phoneId = SubscriptionController.getInstance().getPhoneId(defaultDataSubId); try { Phone defaultDataPhone = PhoneFactory.getPhone(phoneId); if (defaultDataPhone != null && defaultDataPhone.isUserDataEnabled()) { conditions |= OverrideConditions.CONDITION_DEFAULT_DATA_ENABLED; } } catch (IllegalStateException e) { //ignore the exception and do not add the condition Log.d("DataEnabledOverride", e.getMessage()); } } if (TelephonyManager.from(phone.getContext()).isMultiSimEnabled()) { conditions |= OverrideConditions.CONDITION_DSDS_ENABLED; } } return conditions; } /** * Check for given APN type if we should enable data. * * @param phone Phone object * @param apnType APN type * @return {@code true} if data should be enabled for the current condition. */ public boolean shouldOverrideDataEnabledSettings(Phone phone, @ApnType int apnType) { return canSatisfyAnyRule(apnType, getCurrentConditions(phone)); } /** * Get data enabled override rules. * * @return Get data enabled override rules in string format */ @NonNull public String getRules() { List<String> ruleStrings = new ArrayList<>(); for (OverrideRule rule : mRules) { ruleStrings.add(rule.toString()); } return TextUtils.join(",", ruleStrings); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DataEnabledOverride that = (DataEnabledOverride) o; return mRules.equals(that.mRules); } @Override public int hashCode() { return Objects.hash(mRules); } @Override public String toString() { return "DataEnabledOverride: [rules=\"" + getRules() + "\"]"; } }
src/java/com/android/internal/telephony/data/DataNetworkController.java +2 −1 Original line number Diff line number Diff line Loading @@ -1567,7 +1567,8 @@ public class DataNetworkController extends Handler { if (!mDataSettingsManager.isDataEnabled() && networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS) && mDataSettingsManager.isMmsAlwaysAllowed()) { && mDataSettingsManager.isMobileDataPolicyEnabled(TelephonyManager .MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED)) { // We reach here when data is disabled, but MMS always-allowed is enabled. // (Note that isDataEnabled(ApnSetting.TYPE_MMS) returns true in this case, so it // would not generate any soft disallowed reason. We need to explicitly handle it.) Loading
src/java/com/android/internal/telephony/data/DataSettingsManager.java +126 −65 File changed.Preview size limit exceeded, changes collapsed. Show changes
src/java/com/android/internal/telephony/data/DataUtils.java +36 −3 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.telephony.Annotation.NetCapability; import android.telephony.Annotation.NetworkType; import android.telephony.Annotation.ValidationStatus; import android.telephony.TelephonyManager; import android.telephony.TelephonyManager.MobileDataPolicy; import android.telephony.data.ApnSetting; import android.telephony.data.ApnSetting.ApnType; import android.telephony.data.DataCallResponse; Loading @@ -41,6 +42,7 @@ import android.telephony.ims.feature.ImsFeature; import android.util.ArrayMap; import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList; import com.android.telephony.Rlog; import java.text.SimpleDateFormat; import java.util.ArrayList; Loading @@ -61,6 +63,7 @@ public class DataUtils { /** The time format for converting time to readable string. */ private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS", Locale.US); private static final String TAG = "DataUtils"; /** * Get the network capability from the string. Loading Loading @@ -164,6 +167,7 @@ public class DataUtils { case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH: return "PRIORITIZE_BANDWIDTH"; default: loge("Unknown network capability(" + netCap + ")"); return "Unknown(" + netCap + ")"; } } Loading Loading @@ -209,7 +213,9 @@ public class DataUtils { switch (status) { case NetworkAgent.VALIDATION_STATUS_VALID: return "VALID"; case NetworkAgent.VALIDATION_STATUS_NOT_VALID: return "INVALID"; default: return "UNKNOWN(" + status + ")"; default: loge("Unknown validation status(" + status + ")"); return "UNKNOWN(" + status + ")"; } } Loading Loading @@ -366,6 +372,7 @@ public class DataUtils { case ImsFeature.FEATURE_MMTEL: return "MMTEL"; case ImsFeature.FEATURE_RCS: return "RCS"; default: loge("Unknown IMS feature(" + imsFeature + ")"); return "Unknown(" + imsFeature + ")"; } } Loading Loading @@ -468,7 +475,9 @@ public class DataUtils { case DataCallResponse.LINK_STATUS_INACTIVE: return "INACTIVE"; case DataCallResponse.LINK_STATUS_ACTIVE: return "ACTIVE"; case DataCallResponse.LINK_STATUS_DORMANT: return "DORMANT"; default: return "UNKNOWN(" + linkStatus + ")"; default: loge("Unknown link status(" + linkStatus + ")"); return "UNKNOWN(" + linkStatus + ")"; } } Loading Loading @@ -505,7 +514,31 @@ public class DataUtils { case TelephonyManager.DATA_ACTIVITY_OUT: return "OUT"; case TelephonyManager.DATA_ACTIVITY_INOUT: return "INOUT"; case TelephonyManager.DATA_ACTIVITY_DORMANT: return "DORMANT"; default: return "UNKNOWN(" + dataActivity + ")"; default: loge("Unknown data activity(" + dataActivity + ")"); return "UNKNOWN(" + dataActivity + ")"; } } /** * Convert mobile data policy to string. * * @param mobileDataPolicy The mobile data policy. * @return The mobile data policy in string format. */ public static @NonNull String mobileDataPolicyToString(@MobileDataPolicy int mobileDataPolicy) { switch (mobileDataPolicy) { case TelephonyManager.MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL: return "DATA_ON_NON_DEFAULT_DURING_VOICE_CALL"; case TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED: return "MMS_ALWAYS_ALLOWED"; default: loge("Unknown mobile data policy(" + mobileDataPolicy + ")"); return "UNKNOWN(" + mobileDataPolicy + ")"; } } private static void loge(String msg) { Rlog.e(TAG, msg); } }