1 /*
2  * Copyright (C) 2021 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.data;
18 
19 import android.annotation.CurrentTimeMillisLong;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.telephony.TelephonyManager;
23 import android.telephony.data.DataProfile;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 
27 import java.util.ArrayList;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Set;
31 
32 /**
33  * The class to describe a data evaluation for whether allowing or disallowing certain operations
34  * like setup a data network, sustaining existing data networks, or handover between IWLAN and
35  * cellular.
36  */
37 public class DataEvaluation {
38     /** The reason for this evaluation */
39     @NonNull
40     private final DataEvaluationReason mDataEvaluationReason;
41 
42     /** Data disallowed reasons. There could be multiple reasons for not allowing data. */
43     @NonNull
44     private final Set<DataDisallowedReason> mDataDisallowedReasons = new HashSet<>();
45 
46     /** Data allowed reason. It is intended to only have one allowed reason. */
47     @NonNull
48     private DataAllowedReason mDataAllowedReason = DataAllowedReason.NONE;
49 
50     @Nullable
51     private DataProfile mCandidateDataProfile = null;
52 
53     /** The timestamp of evaluation time */
54     @CurrentTimeMillisLong
55     private long mEvaluatedTime = 0;
56 
57     /**
58      * Constructor
59      *
60      * @param reason The reason for this evaluation.
61      */
DataEvaluation(@onNull DataEvaluationReason reason)62     public DataEvaluation(@NonNull DataEvaluationReason reason) {
63         mDataEvaluationReason = reason;
64     }
65 
66     /**
67      * Add a data disallowed reason. Note that adding a disallowed reason will clean up the
68      * allowed reason because they are mutual exclusive.
69      *
70      * @param reason Disallowed reason.
71      */
addDataDisallowedReason(DataDisallowedReason reason)72     public void addDataDisallowedReason(DataDisallowedReason reason) {
73         mDataAllowedReason = DataAllowedReason.NONE;
74         mDataDisallowedReasons.add(reason);
75         mEvaluatedTime = System.currentTimeMillis();
76     }
77 
78     /**
79      * Remove a data disallowed reason if one exists.
80      *
81      * @param reason Disallowed reason.
82      */
removeDataDisallowedReason(DataDisallowedReason reason)83     public void removeDataDisallowedReason(DataDisallowedReason reason) {
84         mDataDisallowedReasons.remove(reason);
85         mEvaluatedTime = System.currentTimeMillis();
86     }
87 
88     /**
89      * Add a data allowed reason. Note that adding an allowed reason will clean up the disallowed
90      * reasons because they are mutual exclusive.
91      *
92      * @param reason Allowed reason.
93      */
addDataAllowedReason(DataAllowedReason reason)94     public void addDataAllowedReason(DataAllowedReason reason) {
95         mDataDisallowedReasons.clear();
96 
97         // Only higher priority allowed reason can overwrite the old one. See
98         // DataAllowedReason for the oder.
99         if (reason.ordinal() > mDataAllowedReason.ordinal()) {
100             mDataAllowedReason = reason;
101         }
102         mEvaluatedTime = System.currentTimeMillis();
103     }
104 
105     /**
106      * @return List of data disallowed reasons.
107      */
108     @NonNull
getDataDisallowedReasons()109     public List<DataDisallowedReason> getDataDisallowedReasons() {
110         return new ArrayList<>(mDataDisallowedReasons);
111     }
112 
113     /**
114      * @return The data allowed reason.
115      */
116     @NonNull
getDataAllowedReason()117     public DataAllowedReason getDataAllowedReason() {
118         return mDataAllowedReason;
119     }
120 
121     /**
122      * Set the candidate data profile for setup data network.
123      *
124      * @param dataProfile The candidate data profile.
125      */
setCandidateDataProfile(@onNull DataProfile dataProfile)126     public void setCandidateDataProfile(@NonNull DataProfile dataProfile) {
127         mCandidateDataProfile = dataProfile;
128     }
129 
130     /**
131      * @return The candidate data profile for setup data network.
132      */
133     @Nullable
getCandidateDataProfile()134     public DataProfile getCandidateDataProfile() {
135         return mCandidateDataProfile;
136     }
137 
138     /**
139      * @return {@code true} if the evaluation contains disallowed reasons.
140      */
containsDisallowedReasons()141     public boolean containsDisallowedReasons() {
142         return !mDataDisallowedReasons.isEmpty();
143     }
144 
145     /**
146      * Check if it contains a certain disallowed reason.
147      *
148      * @param reason The disallowed reason to check.
149      * @return {@code true} if the provided reason matches one of the disallowed reasons.
150      */
contains(DataDisallowedReason reason)151     public boolean contains(DataDisallowedReason reason) {
152         return mDataDisallowedReasons.contains(reason);
153     }
154 
155     /**
156      * Check if only one disallowed reason prevent data connection.
157      *
158      * @param reason The given reason to check
159      * @return {@code true} if the given reason is the only one that prevents data connection
160      */
containsOnly(DataDisallowedReason reason)161     public boolean containsOnly(DataDisallowedReason reason) {
162         return mDataDisallowedReasons.size() == 1 && contains(reason);
163     }
164 
165     /**
166      * Check if all the disallowed reasons are a subset of the given reason.
167      *
168      * @param reasons The given reason to check
169      * @return {@code true} if it doesn't contain any disallowed reasons other than the given
170      * reasons.
171      */
isSubsetOf(DataDisallowedReason... reasons)172     public boolean isSubsetOf(DataDisallowedReason... reasons) {
173         int matched = 0;
174         for (DataDisallowedReason requestedReason : reasons) {
175             if (mDataDisallowedReasons.contains(requestedReason)) matched++;
176         }
177         return matched == mDataDisallowedReasons.size();
178     }
179 
180     /**
181      * Check if the any of the disallowed reasons match one of the provided reason.
182      *
183      * @param reasons The given reasons to check.
184      * @return {@code true} if any of the given reasons matches one of the disallowed reasons.
185      */
containsAny(DataDisallowedReason... reasons)186     public boolean containsAny(DataDisallowedReason... reasons) {
187         for (DataDisallowedReason reason : reasons) {
188             if (mDataDisallowedReasons.contains(reason)) return true;
189         }
190         return false;
191     }
192 
193     /**
194      * Check if the allowed reason is the specified reason.
195      *
196      * @param reason The allowed reason.
197      * @return {@code true} if the specified reason matches the allowed reason.
198      */
contains(DataAllowedReason reason)199     public boolean contains(DataAllowedReason reason) {
200         return reason == mDataAllowedReason;
201     }
202 
203     /**
204      * @return {@code true} if the disallowed reasons contains hard reasons.
205      */
containsHardDisallowedReasons()206     public boolean containsHardDisallowedReasons() {
207         for (DataDisallowedReason reason : mDataDisallowedReasons) {
208             if (reason.isHardReason()) {
209                 return true;
210             }
211         }
212         return false;
213     }
214 
215     /**
216      * The reason for evaluating unsatisfied network requests, existing data networks, and handover.
217      */
218     @VisibleForTesting
219     public enum DataEvaluationReason {
220         /** New request from the apps. */
221         NEW_REQUEST(false),
222         /** Data config changed. */
223         DATA_CONFIG_CHANGED(true),
224         /** SIM is loaded. */
225         SIM_LOADED(true),
226         /** SIM is removed. */
227         SIM_REMOVAL(true),
228         /** Data profiles changed. */
229         DATA_PROFILES_CHANGED(true),
230         /** When service state changes.(For now only considering data RAT and data registration). */
231         DATA_SERVICE_STATE_CHANGED(true),
232         /** When data is enabled or disabled (by user, carrier, thermal, etc...) */
233         DATA_ENABLED_CHANGED(true),
234         /** When data enabled overrides are changed (MMS always allowed, data on non-DDS sub). */
235         DATA_ENABLED_OVERRIDE_CHANGED(true),
236         /** When data roaming is enabled or disabled. */
237         ROAMING_ENABLED_CHANGED(true),
238         /** When voice call ended (for concurrent voice/data not supported RAT). */
239         VOICE_CALL_ENDED(true),
240         /** When network restricts or no longer restricts mobile data. */
241         DATA_RESTRICTED_CHANGED(true),
242         /** Network capabilities changed. The unsatisfied requests might have chances to attach. */
243         DATA_NETWORK_CAPABILITIES_CHANGED(true),
244         /** When emergency call started or ended. */
245         EMERGENCY_CALL_CHANGED(true),
246         /** When data disconnected, re-evaluate later to see if data could be brought up again. */
247         RETRY_AFTER_DISCONNECTED(true),
248         /** Data setup retry. */
249         DATA_RETRY(false),
250         /** For handover evaluation, or for network tearing down after handover succeeds/fails. */
251         DATA_HANDOVER(true),
252         /** Preferred transport changed. */
253         PREFERRED_TRANSPORT_CHANGED(true),
254         /** Slice config changed. */
255         SLICE_CONFIG_CHANGED(true),
256         /** SRVCC state changed. */
257         SRVCC_STATE_CHANGED(true),
258         /**
259          * Single data network arbitration. On certain RATs, only one data network is allowed at the
260          * same time.
261          */
262         SINGLE_DATA_NETWORK_ARBITRATION(true),
263         /** Query from {@link TelephonyManager#isDataConnectivityPossible()}. */
264         EXTERNAL_QUERY(false),
265         /** Tracking area code changed. */
266         TAC_CHANGED(true),
267         /** Unsatisfied network request detached. */
268         UNSATISFIED_REQUEST_DETACHED(true),
269         /** track bootstrap sim data usage */
270         CHECK_DATA_USAGE(false);
271 
272         /**
273          * {@code true} if the evaluation is due to environmental changes (i.e. SIM removal,
274          * registration state changes, etc....
275          */
276         private final boolean mIsConditionBased;
277 
278         /**
279          * @return {@code true} if the evaluation is due to environmental changes (i.e. SIM removal,
280          * registration state changes, etc....
281          */
isConditionBased()282         public boolean isConditionBased() {
283             return mIsConditionBased;
284         }
285 
286         /**
287          * Constructor
288          *
289          * @param isConditionBased {@code true} if the evaluation is due to environmental changes
290          * (i.e. SIM removal, registration state changes, etc....)
291          */
DataEvaluationReason(boolean isConditionBased)292         DataEvaluationReason(boolean isConditionBased) {
293             mIsConditionBased = isConditionBased;
294         }
295     }
296 
297     /** Disallowed reasons. There could be multiple reasons if it is not allowed. */
298     public enum DataDisallowedReason {
299         // Soft failure reasons. A soft reason means that in certain conditions, data is still
300         // allowed. Normally those reasons are due to users settings.
301         /** Data is disabled by the user or policy. */
302         DATA_DISABLED(false),
303         /** Data roaming is disabled by the user. */
304         ROAMING_DISABLED(false),
305         /** Default data not selected. */
306         DEFAULT_DATA_UNSELECTED(false),
307 
308         // Belows are all hard failure reasons. A hard reason means no matter what the data should
309         // not be allowed.
310         /** Data registration state is not in service. */
311         NOT_IN_SERVICE(true),
312         /** Data config is not ready. */
313         DATA_CONFIG_NOT_READY(true),
314         /** SIM is not ready. */
315         SIM_NOT_READY(true),
316         /** Concurrent voice and data is not allowed. */
317         CONCURRENT_VOICE_DATA_NOT_ALLOWED(true),
318         /** Service option not supported. */
319         SERVICE_OPTION_NOT_SUPPORTED(true),
320         /** Carrier notified data should be restricted. */
321         DATA_RESTRICTED_BY_NETWORK(true),
322         /** Radio power is off (i.e. airplane mode on) */
323         RADIO_POWER_OFF(true),
324         /** Data setup now allowed due to pending tear down all networks. */
325         PENDING_TEAR_DOWN_ALL(true),
326         /** Airplane mode is forcibly turned on by the carrier. */
327         RADIO_DISABLED_BY_CARRIER(true),
328         /** Underlying data service is not bound. */
329         DATA_SERVICE_NOT_READY(true),
330         /** Unable to find a suitable data profile. */
331         NO_SUITABLE_DATA_PROFILE(true),
332         /** Current data network type not allowed. */
333         DATA_NETWORK_TYPE_NOT_ALLOWED(true),
334         /** Device is currently in CDMA ECBM. */
335         CDMA_EMERGENCY_CALLBACK_MODE(true),
336         /** There is already a retry setup/handover scheduled. */
337         RETRY_SCHEDULED(true),
338         /** Network has explicitly request to throttle setup attempt. */
339         DATA_THROTTLED(true),
340         /** Data profile becomes invalid. (could be removed by the user, or SIM refresh, etc..) */
341         DATA_PROFILE_INVALID(true),
342         /** Data profile not preferred (i.e. users switch preferred profile in APN editor.) */
343         DATA_PROFILE_NOT_PREFERRED(true),
344         /** Handover is not allowed by policy. */
345         NOT_ALLOWED_BY_POLICY(true),
346         /** Data network is not in the right state. */
347         ILLEGAL_STATE(true),
348         /** VoPS is not supported by the network. */
349         VOPS_NOT_SUPPORTED(true),
350         /** Only one data network is allowed at one time. */
351         ONLY_ALLOWED_SINGLE_NETWORK(true),
352         /** Data enabled settings are not ready. */
353         DATA_SETTINGS_NOT_READY(true),
354         /** Handover max retry stopped but network is not on the preferred transport. */
355         HANDOVER_RETRY_STOPPED(true),
356         /** BootStrap sim data limit reached. */
357         DATA_LIMIT_REACHED(true),
358         /** Data network connectivity transport not allowed. */
359         DATA_NETWORK_TRANSPORT_NOT_ALLOWED(true);
360 
361         private final boolean mIsHardReason;
362 
363         /**
364          * @return {@code true} if the disallowed reason is a hard reason.
365          */
isHardReason()366         public boolean isHardReason() {
367             return mIsHardReason;
368         }
369 
370         /**
371          * Constructor
372          *
373          * @param isHardReason {@code true} if the disallowed reason is a hard reason. A hard reason
374          * means no matter what the data should not be allowed. A soft reason means that in certain
375          * conditions, data is still allowed.
376          */
DataDisallowedReason(boolean isHardReason)377         DataDisallowedReason(boolean isHardReason) {
378             mIsHardReason = isHardReason;
379         }
380     }
381 
382     /**
383      * Data allowed reasons. There will be only one reason if data is allowed.
384      */
385     public enum DataAllowedReason {
386         // Note that unlike disallowed reasons, we only have one allowed reason every time
387         // when we check data is allowed or not. The order of these allowed reasons is very
388         // important. The lower ones take precedence over the upper ones.
389         /**
390          * None. This is the initial value.
391          */
392         NONE,
393         /**
394          * The normal reason. This is the most common case.
395          */
396         NORMAL,
397         /**
398          * Data is allowed because an ongoing VoPS call depends on this network
399          */
400         IN_VOICE_CALL,
401         /**
402          * The network brought up by this network request is unmetered. Should allowed no matter
403          * the user enables or disables data.
404          */
405         UNMETERED_USAGE,
406         /**
407          * The network request supports MMS and MMS is always allowed.
408          */
409         MMS_REQUEST,
410         /**
411          * The network request is restricted (i.e. Only privilege apps can access the network.)
412          */
413         RESTRICTED_REQUEST,
414         /**
415          * SUPL is allowed while emergency call is ongoing.
416          */
417         EMERGENCY_SUPL,
418         /**
419          * Data is allowed because the network request is for emergency. This should be always at
420          * the bottom (i.e. highest priority)
421          */
422         EMERGENCY_REQUEST,
423     }
424 
425     @Override
toString()426     public String toString() {
427         StringBuilder evaluationStr = new StringBuilder();
428         evaluationStr.append("Data evaluation: evaluation reason:")
429                 .append(mDataEvaluationReason).append(", ");
430         if (!mDataDisallowedReasons.isEmpty()) {
431             evaluationStr.append("Data disallowed reasons:");
432             for (DataDisallowedReason reason : mDataDisallowedReasons) {
433                 evaluationStr.append(" ").append(reason);
434             }
435         } else {
436             evaluationStr.append("Data allowed reason:");
437             evaluationStr.append(" ").append(mDataAllowedReason);
438         }
439         evaluationStr.append(", candidate profile=").append(mCandidateDataProfile);
440         evaluationStr.append(", time=").append(DataUtils.systemTimeToString(mEvaluatedTime));
441         return evaluationStr.toString();
442     }
443 
444 }
445