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.Annotation.ApnType;
22 import android.telephony.SubscriptionManager;
23 import android.telephony.TelephonyManager;
24 import android.telephony.data.ApnSetting;
25 import android.text.TextUtils;
26 import android.util.ArrayMap;
27 import android.util.Log;
28 
29 import com.android.internal.annotations.VisibleForTesting;
30 import com.android.internal.telephony.Phone;
31 import com.android.internal.telephony.PhoneConstants;
32 import com.android.internal.telephony.PhoneFactory;
33 import com.android.internal.telephony.SubscriptionController;
34 import com.android.internal.telephony.dataconnection.DataEnabledOverride.OverrideConditions.Condition;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.util.ArrayList;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Objects;
43 import java.util.Set;
44 
45 /**
46  * This class represents the rules for overriding data enabled settings in different conditions.
47  * When data is disabled by the user, data can still be turned on temporarily when conditions
48  * satisfy any rule here.
49  */
50 public class DataEnabledOverride {
51 
52     private final Set<OverrideRule> mRules = new HashSet<>();
53 
54     /**
55      * The rule for allowing data during voice call.
56      */
57     private static final OverrideRule OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL =
58             new OverrideRule(ApnSetting.TYPE_ALL, OverrideConditions.CONDITION_IN_VOICE_CALL
59                     | OverrideConditions.CONDITION_NON_DEFAULT
60                     | OverrideConditions.CONDITION_DEFAULT_DATA_ENABLED
61                     | OverrideConditions.CONDITION_DSDS_ENABLED);
62 
63     /**
64      * The rule for always allowing mms. Without adding any condition to the rule, any condition can
65      * satisfy this rule for mms.
66      */
67     private static final OverrideRule OVERRIDE_RULE_ALWAYS_ALLOW_MMS =
68             new OverrideRule(ApnSetting.TYPE_MMS, OverrideConditions.CONDITION_UNCONDITIONALLY);
69 
70     /**
71      * Data enabled override rule
72      */
73     private static class OverrideRule {
74         /**
75          * APN type of the rule. The rule is APN type specific. The override is applicable to the
76          * specified APN type as well. For now we only support one APN type per rule. Can be
77          * expanded to multiple APN types in the future.
78          */
79         private final @ApnType int mApnType;
80 
81         /** The required conditions for overriding */
82         private final OverrideConditions mRequiredConditions;
83 
84         /**
85          * Constructor
86          *
87          * @param rule The override rule string. For example, {@code mms=nonDefault} or
88          * {@code default=voiceCall & nonDefault}
89          */
OverrideRule(@onNull String rule)90         OverrideRule(@NonNull String rule) {
91             String[] tokens = rule.trim().split("\\s*=\\s*");
92             if (tokens.length != 2) {
93                 throw new IllegalArgumentException("Invalid data enabled override rule format: "
94                         + rule);
95             }
96 
97             if (TextUtils.isEmpty(tokens[0])) {
98                 throw new IllegalArgumentException("APN type can't be empty");
99             }
100 
101             mApnType = ApnSetting.getApnTypesBitmaskFromString(tokens[0]);
102             if (mApnType == ApnSetting.TYPE_NONE) {
103                 throw new IllegalArgumentException("Invalid APN type. Rule=" + rule);
104             }
105 
106             mRequiredConditions = new OverrideConditions(tokens[1]);
107         }
108 
109         /**
110          * Constructor
111          *
112          * @param apnType APN type of the rule
113          * @param requiredConditions The required conditions for the rule
114          */
OverrideRule(int apnType, int requiredConditions)115         private OverrideRule(int apnType, int requiredConditions) {
116             mApnType = apnType;
117             mRequiredConditions = new OverrideConditions(requiredConditions);
118         }
119 
120         /**
121          * Check if this rule can be satisfied by the given APN type and provided conditions.
122          *
123          * @param apnType APN type to check
124          * @param providedConditions The provided conditions to check
125          * @return {@code true} if satisfied
126          */
isSatisfiedByConditions(@pnType int apnType, @Condition int providedConditions)127         boolean isSatisfiedByConditions(@ApnType int apnType, @Condition int providedConditions) {
128             return (mApnType == apnType || mApnType == ApnSetting.TYPE_ALL)
129                     && mRequiredConditions.allMet(providedConditions);
130         }
131 
132         @Override
toString()133         public String toString() {
134             return ApnSetting.getApnTypeString(mApnType) + "=" + mRequiredConditions;
135         }
136 
137         @Override
equals(Object o)138         public boolean equals(Object o) {
139             if (this == o) return true;
140             if (o == null || getClass() != o.getClass()) return false;
141             OverrideRule that = (OverrideRule) o;
142             return mApnType == that.mApnType
143                     && Objects.equals(mRequiredConditions, that.mRequiredConditions);
144         }
145 
146         @Override
hashCode()147         public int hashCode() {
148             return Objects.hash(mApnType, mRequiredConditions);
149         }
150     }
151 
152     /**
153      * Represent the conditions for overriding data enabled settings
154      */
155     static class OverrideConditions {
156         // Possible values for data enabled override condition. Note these flags are bitmasks.
157         /** Unconditionally override enabled settings */
158         static final int CONDITION_UNCONDITIONALLY = 0;
159 
160         /** Enable data only on subscription that is not user selected default data subscription */
161         static final int CONDITION_NON_DEFAULT = 1 << 0;
162 
163         /** Enable data only when device has ongoing voice call */
164         static final int CONDITION_IN_VOICE_CALL = 1 << 1;
165 
166         /** Enable data only when default data is on */
167         static final int CONDITION_DEFAULT_DATA_ENABLED = 1 << 2;
168 
169         /** Enable data only when device is in DSDS mode */
170         static final int CONDITION_DSDS_ENABLED = 1 << 3;
171 
172         /** Enable data unconditionally in string format */
173         static final String CONDITION_UNCONDITIONALLY_STRING = "unconditionally";
174 
175         /** Enable data only on subscription that is not default in string format */
176         static final String CONDITION_NON_DEFAULT_STRING = "nonDefault";
177 
178         /** Enable data only when device has ongoing voice call in string format */
179         static final String CONDITION_VOICE_CALL_STRING = "inVoiceCall";
180 
181         /** Enable data only when default data is on in string format */
182         static final String CONDITION_DEFAULT_DATA_ENABLED_STRING = "DefaultDataOn";
183 
184         /** Enable data only when device is in DSDS mode in string format */
185         static final String CONDITION_DSDS_ENABLED_STRING = "dsdsEnabled";
186 
187         /** @hide */
188         @IntDef(flag = true, prefix = { "OVERRIDE_CONDITION_" }, value = {
189                 CONDITION_NON_DEFAULT,
190                 CONDITION_IN_VOICE_CALL,
191                 CONDITION_DEFAULT_DATA_ENABLED,
192                 CONDITION_DSDS_ENABLED
193         })
194         @Retention(RetentionPolicy.SOURCE)
195         public @interface Condition {}
196 
197         private static final Map<Integer, String> OVERRIDE_CONDITION_INT_MAP = new ArrayMap<>();
198         private static final Map<String, Integer> OVERRIDE_CONDITION_STRING_MAP = new ArrayMap<>();
199 
200         static {
OVERRIDE_CONDITION_INT_MAP.put(CONDITION_NON_DEFAULT, CONDITION_NON_DEFAULT_STRING)201             OVERRIDE_CONDITION_INT_MAP.put(CONDITION_NON_DEFAULT,
202                     CONDITION_NON_DEFAULT_STRING);
OVERRIDE_CONDITION_INT_MAP.put(CONDITION_IN_VOICE_CALL, CONDITION_VOICE_CALL_STRING)203             OVERRIDE_CONDITION_INT_MAP.put(CONDITION_IN_VOICE_CALL,
204                     CONDITION_VOICE_CALL_STRING);
OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DEFAULT_DATA_ENABLED, CONDITION_DEFAULT_DATA_ENABLED_STRING)205             OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DEFAULT_DATA_ENABLED,
206                     CONDITION_DEFAULT_DATA_ENABLED_STRING);
OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DSDS_ENABLED, CONDITION_DSDS_ENABLED_STRING)207             OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DSDS_ENABLED,
208                     CONDITION_DSDS_ENABLED_STRING);
209 
OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_UNCONDITIONALLY_STRING, CONDITION_UNCONDITIONALLY)210             OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_UNCONDITIONALLY_STRING,
211                     CONDITION_UNCONDITIONALLY);
OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_NON_DEFAULT_STRING, CONDITION_NON_DEFAULT)212             OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_NON_DEFAULT_STRING,
213                     CONDITION_NON_DEFAULT);
OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_VOICE_CALL_STRING, CONDITION_IN_VOICE_CALL)214             OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_VOICE_CALL_STRING,
215                     CONDITION_IN_VOICE_CALL);
OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DEFAULT_DATA_ENABLED_STRING, CONDITION_DEFAULT_DATA_ENABLED)216             OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DEFAULT_DATA_ENABLED_STRING,
217                     CONDITION_DEFAULT_DATA_ENABLED);
OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DSDS_ENABLED_STRING, CONDITION_DSDS_ENABLED)218             OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DSDS_ENABLED_STRING,
219                     CONDITION_DSDS_ENABLED);
220         }
221 
222         private final @Condition int mConditions;
223 
224         /**
225          * Conditions for overriding data enabled setting
226          *
227          * @param conditions Conditions in string format
228          */
OverrideConditions(@onNull String conditions)229         OverrideConditions(@NonNull String conditions) {
230             mConditions = getBitmaskFromString(conditions);
231         }
232 
233         /**
234          * Conditions for overriding data enabled setting
235          *
236          * @param conditions Conditions in bitmask
237          */
OverrideConditions(@ondition int conditions)238         OverrideConditions(@Condition int conditions) {
239             mConditions = conditions;
240         }
241 
getStringFromBitmask(@ondition int conditions)242         private static String getStringFromBitmask(@Condition int conditions) {
243             if (conditions == CONDITION_UNCONDITIONALLY) {
244                 return CONDITION_UNCONDITIONALLY_STRING;
245             }
246             List<String> conditionsStrings = new ArrayList<>();
247             for (Integer condition : OVERRIDE_CONDITION_INT_MAP.keySet()) {
248                 if ((conditions & condition) == condition) {
249                     conditionsStrings.add(OVERRIDE_CONDITION_INT_MAP.get(condition));
250                 }
251             }
252             return TextUtils.join("&", conditionsStrings);
253         }
254 
getBitmaskFromString(@onNull String str)255         private static @Condition int getBitmaskFromString(@NonNull String str) {
256             if (TextUtils.isEmpty(str)) {
257                 throw new IllegalArgumentException("Empty rule string");
258             }
259 
260             String[] conditionStrings = str.trim().split("\\s*&\\s*");
261             int bitmask = 0;
262 
263             for (String conditionStr : conditionStrings) {
264                 if (!TextUtils.isEmpty(conditionStr)) {
265                     if (!OVERRIDE_CONDITION_STRING_MAP.containsKey(conditionStr)) {
266                         throw new IllegalArgumentException("Invalid conditions: " + str);
267                     }
268                     bitmask |= OVERRIDE_CONDITION_STRING_MAP.get(conditionStr);
269                 }
270             }
271 
272             return bitmask;
273         }
274 
275         /**
276          * Check if provided conditions can meet all conditions in the rule.
277          *
278          * @param providedConditions The provided conditions
279          * @return {@code true} if all conditions are met.
280          */
allMet(@ondition int providedConditions)281         boolean allMet(@Condition int providedConditions) {
282             return (providedConditions & mConditions) == mConditions;
283         }
284 
285         @Override
equals(Object o)286         public boolean equals(Object o) {
287             if (this == o) return true;
288             if (o == null || getClass() != o.getClass()) return false;
289             OverrideConditions that = (OverrideConditions) o;
290             return mConditions == that.mConditions;
291         }
292 
293         @Override
hashCode()294         public int hashCode() {
295             return Objects.hash(mConditions);
296         }
297 
298         @Override
toString()299         public String toString() {
300             return getStringFromBitmask(mConditions);
301         }
302     }
303 
304     /**
305      * Constructor
306      *
307      * @param rules Data enabled override rules
308      */
DataEnabledOverride(@onNull String rules)309     public DataEnabledOverride(@NonNull String rules) {
310         updateRules(rules);
311     }
312 
313     /**
314      * Update the data enabled override rules.
315      *
316      * @param newRules New override rules
317      */
318     @VisibleForTesting
updateRules(@onNull String newRules)319     public void updateRules(@NonNull String newRules) {
320         mRules.clear();
321         String[] rulesString = newRules.trim().split("\\s*,\\s*");
322         for (String rule : rulesString) {
323             if (!TextUtils.isEmpty(rule)) {
324                 mRules.add(new OverrideRule(rule));
325             }
326         }
327     }
328 
329     /**
330      * Set always allowing MMS
331      *
332      * @param allow {@code true} if always allowing, otherwise {@code false}.
333      */
setAlwaysAllowMms(boolean allow)334     public void setAlwaysAllowMms(boolean allow) {
335         if (allow) {
336             mRules.add(OVERRIDE_RULE_ALWAYS_ALLOW_MMS);
337         } else {
338             mRules.remove(OVERRIDE_RULE_ALWAYS_ALLOW_MMS);
339         }
340     }
341 
342     /**
343      * Set allowing mobile data during voice call. This is used for allowing data on the non-default
344      * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will
345      * not be able to use mobile data. By calling this API, data will be temporarily enabled on the
346      * non-default data SIM during the life cycle of the voice call.
347      *
348      * @param allow {@code true} if allowing using data during voice call, {@code false} if
349      * disallowed.
350      */
setDataAllowedInVoiceCall(boolean allow)351     public void setDataAllowedInVoiceCall(boolean allow) {
352         if (allow) {
353             mRules.add(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
354         } else {
355             mRules.remove(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
356         }
357     }
358 
359     /**
360      * Check if data is allowed during voice call.
361      *
362      * @return {@code true} if data is allowed during voice call.
363      */
isDataAllowedInVoiceCall()364     public boolean isDataAllowedInVoiceCall() {
365         return mRules.contains(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
366     }
367 
canSatisfyAnyRule(@pnType int apnType, @Condition int providedConditions)368     private boolean canSatisfyAnyRule(@ApnType int apnType,
369                                       @Condition int providedConditions) {
370         for (OverrideRule rule : mRules) {
371             if (rule.isSatisfiedByConditions(apnType, providedConditions)) {
372                 return true;
373             }
374         }
375         return false;
376     }
377 
getCurrentConditions(Phone phone)378     private @Condition int getCurrentConditions(Phone phone) {
379         int conditions = 0;
380 
381         if (phone != null) {
382             // Check if the device is on voice call
383             if (phone.getState() != PhoneConstants.State.IDLE) {
384                 conditions |= OverrideConditions.CONDITION_IN_VOICE_CALL;
385             }
386 
387             int defaultDataSubId = SubscriptionController.getInstance().getDefaultDataSubId();
388 
389             if (phone.getSubId() != defaultDataSubId) {
390                 conditions |= OverrideConditions.CONDITION_NON_DEFAULT;
391             }
392 
393             if (defaultDataSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
394                 int phoneId = SubscriptionController.getInstance().getPhoneId(defaultDataSubId);
395                 try {
396                     Phone defaultDataPhone = PhoneFactory.getPhone(phoneId);
397                     if (defaultDataPhone != null && defaultDataPhone.isUserDataEnabled()) {
398                         conditions |= OverrideConditions.CONDITION_DEFAULT_DATA_ENABLED;
399                     }
400                 } catch (IllegalStateException e) {
401                     //ignore the exception and do not add the condition
402                     Log.d("DataEnabledOverride", e.getMessage());
403                 }
404             }
405 
406             if (TelephonyManager.from(phone.getContext()).isMultiSimEnabled()) {
407                 conditions |= OverrideConditions.CONDITION_DSDS_ENABLED;
408             }
409         }
410 
411         return conditions;
412     }
413 
414     /**
415      * Check for given APN type if we should enable data.
416      *
417      * @param phone Phone object
418      * @param apnType APN type
419      * @return {@code true} if data should be enabled for the current condition.
420      */
shouldOverrideDataEnabledSettings(Phone phone, @ApnType int apnType)421     public boolean shouldOverrideDataEnabledSettings(Phone phone, @ApnType int apnType) {
422         return canSatisfyAnyRule(apnType, getCurrentConditions(phone));
423     }
424 
425     /**
426      * Get data enabled override rules.
427      *
428      * @return Get data enabled override rules in string format
429      */
430     @NonNull
getRules()431     public String getRules() {
432         List<String> ruleStrings = new ArrayList<>();
433         for (OverrideRule rule : mRules) {
434             ruleStrings.add(rule.toString());
435         }
436         return TextUtils.join(",", ruleStrings);
437     }
438 
439     @Override
equals(Object o)440     public boolean equals(Object o) {
441         if (this == o) return true;
442         if (o == null || getClass() != o.getClass()) return false;
443         DataEnabledOverride that = (DataEnabledOverride) o;
444         return mRules.equals(that.mRules);
445     }
446 
447     @Override
hashCode()448     public int hashCode() {
449         return Objects.hash(mRules);
450     }
451 
452     @Override
toString()453     public String toString() {
454         return "DataEnabledOverride: [rules=\"" + getRules() + "\"]";
455     }
456 }
457