1 /* 2 * Copyright 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony.dataconnection; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.telephony.data.ApnSetting; 22 import android.telephony.data.ApnSetting.ApnType; 23 import android.text.TextUtils; 24 import android.util.ArrayMap; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 import com.android.internal.telephony.Phone; 28 import com.android.internal.telephony.PhoneConstants; 29 import com.android.internal.telephony.SubscriptionController; 30 import com.android.internal.telephony.dataconnection.DataEnabledOverride.OverrideConditions.Condition; 31 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 import java.util.ArrayList; 35 import java.util.HashSet; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Objects; 39 import java.util.Set; 40 41 /** 42 * This class represents the rules for overriding data enabled settings in different conditions. 43 * When data is disabled by the user, data can still be turned on temporarily when conditions 44 * satisfy any rule here. 45 */ 46 public class DataEnabledOverride { 47 48 private final Set<OverrideRule> mRules = new HashSet<>(); 49 50 /** 51 * The rule for allowing data during voice call. 52 */ 53 private static final OverrideRule OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL = 54 new OverrideRule(ApnSetting.TYPE_ALL, OverrideConditions.CONDITION_IN_VOICE_CALL 55 | OverrideConditions.CONDITION_NON_DEFAULT); 56 57 /** 58 * The rule for always allowing mms. Without adding any condition to the rule, any condition can 59 * satisfy this rule for mms. 60 */ 61 private static final OverrideRule OVERRIDE_RULE_ALWAYS_ALLOW_MMS = 62 new OverrideRule(ApnSetting.TYPE_MMS, OverrideConditions.CONDITION_UNCONDITIONALLY); 63 64 /** 65 * Data enabled override rule 66 */ 67 private static class OverrideRule { 68 /** 69 * APN type of the rule. The rule is APN type specific. The override is applicable to the 70 * specified APN type as well. For now we only support one APN type per rule. Can be 71 * expanded to multiple APN types in the future. 72 */ 73 private final @ApnType int mApnType; 74 75 /** The required conditions for overriding */ 76 private final OverrideConditions mRequiredConditions; 77 78 /** 79 * Constructor 80 * 81 * @param rule The override rule string. For example, {@code mms=nonDefault} or 82 * {@code default=voiceCall & nonDefault} 83 */ OverrideRule(@onNull String rule)84 OverrideRule(@NonNull String rule) { 85 String[] tokens = rule.trim().split("\\s*=\\s*"); 86 if (tokens.length != 2) { 87 throw new IllegalArgumentException("Invalid data enabled override rule format: " 88 + rule); 89 } 90 91 if (TextUtils.isEmpty(tokens[0])) { 92 throw new IllegalArgumentException("APN type can't be empty"); 93 } 94 95 mApnType = ApnSetting.getApnTypesBitmaskFromString(tokens[0]); 96 if (mApnType == ApnSetting.TYPE_NONE) { 97 throw new IllegalArgumentException("Invalid APN type. Rule=" + rule); 98 } 99 100 mRequiredConditions = new OverrideConditions(tokens[1]); 101 } 102 103 /** 104 * Constructor 105 * 106 * @param apnType APN type of the rule 107 * @param requiredConditions The required conditions for the rule 108 */ OverrideRule(int apnType, int requiredConditions)109 private OverrideRule(int apnType, int requiredConditions) { 110 mApnType = apnType; 111 mRequiredConditions = new OverrideConditions(requiredConditions); 112 } 113 114 /** 115 * Check if this rule can be satisfied by the given APN type and provided conditions. 116 * 117 * @param apnType APN type to check 118 * @param providedConditions The provided conditions to check 119 * @return {@code true} if satisfied 120 */ isSatisfiedByConditions(@pnType int apnType, @Condition int providedConditions)121 boolean isSatisfiedByConditions(@ApnType int apnType, @Condition int providedConditions) { 122 return (mApnType == apnType || mApnType == ApnSetting.TYPE_ALL) 123 && mRequiredConditions.allMet(providedConditions); 124 } 125 126 @Override toString()127 public String toString() { 128 return ApnSetting.getApnTypeString(mApnType) + "=" + mRequiredConditions; 129 } 130 131 @Override equals(Object o)132 public boolean equals(Object o) { 133 if (this == o) return true; 134 if (o == null || getClass() != o.getClass()) return false; 135 OverrideRule that = (OverrideRule) o; 136 return mApnType == that.mApnType 137 && Objects.equals(mRequiredConditions, that.mRequiredConditions); 138 } 139 140 @Override hashCode()141 public int hashCode() { 142 return Objects.hash(mApnType, mRequiredConditions); 143 } 144 } 145 146 /** 147 * Represent the conditions for overriding data enabled settings 148 */ 149 static class OverrideConditions { 150 // Possible values for data enabled override condition. Note these flags are bitmasks. 151 /** Unconditionally override enabled settings */ 152 static final int CONDITION_UNCONDITIONALLY = 0; 153 154 /** Enable data only on subscription that is not user selected default data subscription */ 155 static final int CONDITION_NON_DEFAULT = 1 << 0; 156 157 /** Enable data only when device has ongoing voice call */ 158 static final int CONDITION_IN_VOICE_CALL = 1 << 1; 159 160 /** Enable data unconditionally in string format */ 161 static final String CONDITION_UNCONDITIONALLY_STRING = "unconditionally"; 162 163 /** Enable data only on subscription that is not default in string format */ 164 static final String CONDITION_NON_DEFAULT_STRING = "nonDefault"; 165 166 /** Enable data only when device has ongoing voice call in string format */ 167 static final String CONDITION_VOICE_CALL_STRING = "inVoiceCall"; 168 169 /** @hide */ 170 @IntDef(flag = true, prefix = { "OVERRIDE_CONDITION_" }, value = { 171 CONDITION_NON_DEFAULT, 172 CONDITION_IN_VOICE_CALL, 173 }) 174 @Retention(RetentionPolicy.SOURCE) 175 public @interface Condition {} 176 177 private static final Map<Integer, String> OVERRIDE_CONDITION_INT_MAP = new ArrayMap<>(); 178 private static final Map<String, Integer> OVERRIDE_CONDITION_STRING_MAP = new ArrayMap<>(); 179 180 static { OVERRIDE_CONDITION_INT_MAP.put(CONDITION_NON_DEFAULT, CONDITION_NON_DEFAULT_STRING)181 OVERRIDE_CONDITION_INT_MAP.put(CONDITION_NON_DEFAULT, 182 CONDITION_NON_DEFAULT_STRING); OVERRIDE_CONDITION_INT_MAP.put(CONDITION_IN_VOICE_CALL, CONDITION_VOICE_CALL_STRING)183 OVERRIDE_CONDITION_INT_MAP.put(CONDITION_IN_VOICE_CALL, 184 CONDITION_VOICE_CALL_STRING); 185 OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_UNCONDITIONALLY_STRING, CONDITION_UNCONDITIONALLY)186 OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_UNCONDITIONALLY_STRING, 187 CONDITION_UNCONDITIONALLY); OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_NON_DEFAULT_STRING, CONDITION_NON_DEFAULT)188 OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_NON_DEFAULT_STRING, 189 CONDITION_NON_DEFAULT); OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_VOICE_CALL_STRING, CONDITION_IN_VOICE_CALL)190 OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_VOICE_CALL_STRING, 191 CONDITION_IN_VOICE_CALL); 192 } 193 194 private final @Condition int mConditions; 195 196 /** 197 * Conditions for overriding data enabled setting 198 * 199 * @param conditions Conditions in string format 200 */ OverrideConditions(@onNull String conditions)201 OverrideConditions(@NonNull String conditions) { 202 mConditions = getBitmaskFromString(conditions); 203 } 204 205 /** 206 * Conditions for overriding data enabled setting 207 * 208 * @param conditions Conditions in bitmask 209 */ OverrideConditions(@ondition int conditions)210 OverrideConditions(@Condition int conditions) { 211 mConditions = conditions; 212 } 213 getStringFromBitmask(@ondition int conditions)214 private static String getStringFromBitmask(@Condition int conditions) { 215 if (conditions == CONDITION_UNCONDITIONALLY) { 216 return CONDITION_UNCONDITIONALLY_STRING; 217 } 218 List<String> conditionsStrings = new ArrayList<>(); 219 for (Integer condition : OVERRIDE_CONDITION_INT_MAP.keySet()) { 220 if ((conditions & condition) == condition) { 221 conditionsStrings.add(OVERRIDE_CONDITION_INT_MAP.get(condition)); 222 } 223 } 224 return TextUtils.join("&", conditionsStrings); 225 } 226 getBitmaskFromString(@onNull String str)227 private static @Condition int getBitmaskFromString(@NonNull String str) { 228 if (TextUtils.isEmpty(str)) { 229 throw new IllegalArgumentException("Empty rule string"); 230 } 231 232 String[] conditionStrings = str.trim().split("\\s*&\\s*"); 233 int bitmask = 0; 234 235 for (String conditionStr : conditionStrings) { 236 if (!TextUtils.isEmpty(conditionStr)) { 237 if (!OVERRIDE_CONDITION_STRING_MAP.containsKey(conditionStr)) { 238 throw new IllegalArgumentException("Invalid conditions: " + str); 239 } 240 bitmask |= OVERRIDE_CONDITION_STRING_MAP.get(conditionStr); 241 } 242 } 243 244 return bitmask; 245 } 246 247 /** 248 * Check if provided conditions can meet all conditions in the rule. 249 * 250 * @param providedConditions The provided conditions 251 * @return {@code true} if all conditions are met. 252 */ allMet(@ondition int providedConditions)253 boolean allMet(@Condition int providedConditions) { 254 return (providedConditions & mConditions) == mConditions; 255 } 256 257 @Override equals(Object o)258 public boolean equals(Object o) { 259 if (this == o) return true; 260 if (o == null || getClass() != o.getClass()) return false; 261 OverrideConditions that = (OverrideConditions) o; 262 return mConditions == that.mConditions; 263 } 264 265 @Override hashCode()266 public int hashCode() { 267 return Objects.hash(mConditions); 268 } 269 270 @Override toString()271 public String toString() { 272 return getStringFromBitmask(mConditions); 273 } 274 } 275 276 /** 277 * Constructor 278 * 279 * @param rules Data enabled override rules 280 */ DataEnabledOverride(@onNull String rules)281 public DataEnabledOverride(@NonNull String rules) { 282 updateRules(rules); 283 } 284 285 /** 286 * Update the data enabled override rules. 287 * 288 * @param newRules New override rules 289 */ 290 @VisibleForTesting updateRules(@onNull String newRules)291 public void updateRules(@NonNull String newRules) { 292 mRules.clear(); 293 String[] rulesString = newRules.trim().split("\\s*,\\s*"); 294 for (String rule : rulesString) { 295 if (!TextUtils.isEmpty(rule)) { 296 mRules.add(new OverrideRule(rule)); 297 } 298 } 299 } 300 301 /** 302 * Set always allowing MMS 303 * 304 * @param allow {@code true} if always allowing, otherwise {@code false}. 305 */ setAlwaysAllowMms(boolean allow)306 public void setAlwaysAllowMms(boolean allow) { 307 if (allow) { 308 mRules.add(OVERRIDE_RULE_ALWAYS_ALLOW_MMS); 309 } else { 310 mRules.remove(OVERRIDE_RULE_ALWAYS_ALLOW_MMS); 311 } 312 } 313 314 /** 315 * Set allowing mobile data during voice call. 316 * 317 * @param allow {@code true} if allowing using data during voice call, {@code false} if 318 * disallowed. 319 */ setDataAllowedInVoiceCall(boolean allow)320 public void setDataAllowedInVoiceCall(boolean allow) { 321 if (allow) { 322 mRules.add(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL); 323 } else { 324 mRules.remove(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL); 325 } 326 } 327 328 /** 329 * Check if data is allowed during voice call. 330 * 331 * @return {@code true} if data is allowed during voice call. 332 */ isDataAllowedInVoiceCall()333 public boolean isDataAllowedInVoiceCall() { 334 return mRules.contains(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL); 335 } 336 canSatisfyAnyRule(@pnType int apnType, @Condition int providedConditions)337 private boolean canSatisfyAnyRule(@ApnType int apnType, 338 @Condition int providedConditions) { 339 for (OverrideRule rule : mRules) { 340 if (rule.isSatisfiedByConditions(apnType, providedConditions)) { 341 return true; 342 } 343 } 344 return false; 345 } 346 getCurrentConditions(Phone phone)347 private @Condition int getCurrentConditions(Phone phone) { 348 int conditions = 0; 349 350 if (phone != null) { 351 // Check if the device is on voice call 352 if (phone.getState() != PhoneConstants.State.IDLE) { 353 conditions |= OverrideConditions.CONDITION_IN_VOICE_CALL; 354 } 355 356 if (phone.getSubId() != SubscriptionController.getInstance().getDefaultDataSubId()) { 357 conditions |= OverrideConditions.CONDITION_NON_DEFAULT; 358 } 359 } 360 361 return conditions; 362 } 363 364 /** 365 * Check for given APN type if we should enable data. 366 * 367 * @param phone Phone object 368 * @param apnType APN type 369 * @return {@code true} if data should be enabled for the current condition. 370 */ shouldOverrideDataEnabledSettings(Phone phone, @ApnType int apnType)371 public boolean shouldOverrideDataEnabledSettings(Phone phone, @ApnType int apnType) { 372 return canSatisfyAnyRule(apnType, getCurrentConditions(phone)); 373 } 374 375 /** 376 * Get data enabled override rules. 377 * 378 * @return Get data enabled override rules in string format 379 */ 380 @NonNull getRules()381 public String getRules() { 382 List<String> ruleStrings = new ArrayList<>(); 383 for (OverrideRule rule : mRules) { 384 ruleStrings.add(rule.toString()); 385 } 386 return TextUtils.join(",", ruleStrings); 387 } 388 389 @Override equals(Object o)390 public boolean equals(Object o) { 391 if (this == o) return true; 392 if (o == null || getClass() != o.getClass()) return false; 393 DataEnabledOverride that = (DataEnabledOverride) o; 394 return mRules.equals(that.mRules); 395 } 396 397 @Override hashCode()398 public int hashCode() { 399 return Objects.hash(mRules); 400 } 401 402 @Override toString()403 public String toString() { 404 return "DataEnabledOverride: [rules=\"" + getRules() + "\"]"; 405 } 406 } 407