• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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