aboutsummaryrefslogtreecommitdiff
path: root/src/java/com/android/internal/telephony/data/DataEvaluation.java
blob: 6858c6a0320225fb91aa21e4a546b75119e5f571 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
/*
 * 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.CurrentTimeMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.telephony.TelephonyManager;
import android.telephony.data.DataProfile;

import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * The class to describe a data evaluation for whether allowing or disallowing certain operations
 * like setup a data network, sustaining existing data networks, or handover between IWLAN and
 * cellular.
 */
public class DataEvaluation {
    /** The reason for this evaluation */
    private final @NonNull DataEvaluationReason mDataEvaluationReason;

    /** Data disallowed reasons. There could be multiple reasons for not allowing data. */
    private final @NonNull Set<DataDisallowedReason> mDataDisallowedReasons = new HashSet<>();

    /** Data allowed reason. It is intended to only have one allowed reason. */
    private @NonNull DataAllowedReason mDataAllowedReason = DataAllowedReason.NONE;

    private @Nullable DataProfile mCandidateDataProfile = null;

    /** The timestamp of evaluation time */
    private @CurrentTimeMillisLong long mEvaluatedTime = 0;

    /**
     * Constructor
     *
     * @param reason The reason for this evaluation.
     */
    public DataEvaluation(DataEvaluationReason reason) {
        mDataEvaluationReason = reason;
    }

    /**
     * Add a data disallowed reason. Note that adding a disallowed reason will clean up the
     * allowed reason because they are mutual exclusive.
     *
     * @param reason Disallowed reason.
     */
    public void addDataDisallowedReason(DataDisallowedReason reason) {
        mDataAllowedReason = DataAllowedReason.NONE;
        mDataDisallowedReasons.add(reason);
        mEvaluatedTime = System.currentTimeMillis();
    }

    /**
     * Remove a data disallowed reason if one exists.
     *
     * @param reason Disallowed reason.
     */
    public void removeDataDisallowedReason(DataDisallowedReason reason) {
        mDataDisallowedReasons.remove(reason);
        mEvaluatedTime = System.currentTimeMillis();
    }

    /**
     * Add a data allowed reason. Note that adding an allowed reason will clean up the disallowed
     * reasons because they are mutual exclusive.
     *
     * @param reason Allowed reason.
     */
    public void addDataAllowedReason(DataAllowedReason reason) {
        mDataDisallowedReasons.clear();

        // Only higher priority allowed reason can overwrite the old one. See
        // DataAllowedReason for the oder.
        if (reason.ordinal() > mDataAllowedReason.ordinal()) {
            mDataAllowedReason = reason;
        }
        mEvaluatedTime = System.currentTimeMillis();
    }

    /**
     * @return List of data disallowed reasons.
     */
    public @NonNull List<DataDisallowedReason> getDataDisallowedReasons() {
        return new ArrayList<>(mDataDisallowedReasons);
    }

    /**
     * @return The data allowed reason.
     */
    public @NonNull DataAllowedReason getDataAllowedReason() {
        return mDataAllowedReason;
    }

    /**
     * Set the candidate data profile for setup data network.
     *
     * @param dataProfile The candidate data profile.
     */
    public void setCandidateDataProfile(@NonNull DataProfile dataProfile) {
        mCandidateDataProfile = dataProfile;
    }

    /**
     * @return The candidate data profile for setup data network.
     */
    public @Nullable DataProfile getCandidateDataProfile() {
        return mCandidateDataProfile;
    }

    /**
     * @return {@code true} if the evaluation contains disallowed reasons.
     */
    public boolean containsDisallowedReasons() {
        return mDataDisallowedReasons.size() != 0;
    }

    /**
     * Check if it contains a certain disallowed reason.
     *
     * @param reason The disallowed reason to check.
     * @return {@code true} if the provided reason matches one of the disallowed reasons.
     */
    public boolean contains(DataDisallowedReason reason) {
        return mDataDisallowedReasons.contains(reason);
    }

    /**
     * Check if only one disallowed reason prevent data connection.
     *
     * @param reason The given reason to check
     * @return {@code true} if the given reason is the only one that prevents data connection
     */
    public boolean containsOnly(DataDisallowedReason reason) {
        return mDataDisallowedReasons.size() == 1 && contains(reason);
    }

    /**
     * Check if all the disallowed reasons are a subset of the given reason.
     *
     * @param reasons The given reason to check
     * @return {@code true} if it doesn't contain any disallowed reasons other than the given
     * reasons.
     */
    public boolean isSubsetOf(DataDisallowedReason... reasons) {
        int matched = 0;
        for (DataDisallowedReason requestedReason : reasons) {
            if (mDataDisallowedReasons.contains(requestedReason)) matched++;
        }
        return matched == mDataDisallowedReasons.size();
    }

    /**
     * Check if the any of the disallowed reasons match one of the provided reason.
     *
     * @param reasons The given reasons to check.
     * @return {@code true} if any of the given reasons matches one of the disallowed reasons.
     */
    public boolean containsAny(DataDisallowedReason... reasons) {
        for (DataDisallowedReason reason : reasons) {
            if (mDataDisallowedReasons.contains(reason)) return true;
        }
        return false;
    }

    /**
     * Check if the allowed reason is the specified reason.
     *
     * @param reason The allowed reason.
     * @return {@code true} if the specified reason matches the allowed reason.
     */
    public boolean contains(DataAllowedReason reason) {
        return reason == mDataAllowedReason;
    }

    /**
     * @return {@code true} if the disallowed reasons contains hard reasons.
     */
    public boolean containsHardDisallowedReasons() {
        for (DataDisallowedReason reason : mDataDisallowedReasons) {
            if (reason.isHardReason()) {
                return true;
            }
        }
        return false;
    }

    /**
     * The reason for evaluating unsatisfied network requests, existing data networks, and handover.
     */
    @VisibleForTesting
    public enum DataEvaluationReason {
        /** New request from the apps. */
        NEW_REQUEST(false),
        /** Data config changed. */
        DATA_CONFIG_CHANGED(true),
        /** SIM is loaded. */
        SIM_LOADED(true),
        /** SIM is removed. */
        SIM_REMOVAL(true),
        /** Data profiles changed. */
        DATA_PROFILES_CHANGED(true),
        /** When service state changes.(For now only considering data RAT and data registration). */
        DATA_SERVICE_STATE_CHANGED(true),
        /** When data is enabled or disabled (by user, carrier, thermal, etc...) */
        DATA_ENABLED_CHANGED(true),
        /** When data enabled overrides are changed (MMS always allowed, data on non-DDS sub). */
        DATA_ENABLED_OVERRIDE_CHANGED(true),
        /** When data roaming is enabled or disabled. */
        ROAMING_ENABLED_CHANGED(true),
        /** When voice call ended (for concurrent voice/data not supported RAT). */
        VOICE_CALL_ENDED(true),
        /** When network restricts or no longer restricts mobile data. */
        DATA_RESTRICTED_CHANGED(true),
        /** Network capabilities changed. The unsatisfied requests might have chances to attach. */
        DATA_NETWORK_CAPABILITIES_CHANGED(true),
        /** When emergency call started or ended. */
        EMERGENCY_CALL_CHANGED(true),
        /** When data disconnected, re-evaluate later to see if data could be brought up again. */
        RETRY_AFTER_DISCONNECTED(true),
        /** Data setup retry. */
        DATA_RETRY(false),
        /** For handover evaluation, or for network tearing down after handover succeeds/fails. */
        DATA_HANDOVER(true),
        /** Preferred transport changed. */
        PREFERRED_TRANSPORT_CHANGED(true),
        /** Slice config changed. */
        SLICE_CONFIG_CHANGED(true),
        /** SRVCC state changed. */
        SRVCC_STATE_CHANGED(true),
        /**
         * Single data network arbitration. On certain RATs, only one data network is allowed at the
         * same time.
         */
        SINGLE_DATA_NETWORK_ARBITRATION(true),
        /** Query from {@link TelephonyManager#isDataConnectivityPossible()}. */
        EXTERNAL_QUERY(false),
        /** Tracking area code changed. */
        TAC_CHANGED(true),
        /** Unsatisfied network request detached. */
        UNSATISFIED_REQUEST_DETACHED(true);

        /**
         * {@code true} if the evaluation is due to environmental changes (i.e. SIM removal,
         * registration state changes, etc....
         */
        private final boolean mIsConditionBased;

        /**
         * @return {@code true} if the evaluation is due to environmental changes (i.e. SIM removal,
         * registration state changes, etc....
         */
        public boolean isConditionBased() {
            return mIsConditionBased;
        }

        /**
         * Constructor
         *
         * @param isConditionBased {@code true} if the evaluation is due to environmental changes
         * (i.e. SIM removal, registration state changes, etc....)
         */
        DataEvaluationReason(boolean isConditionBased) {
            mIsConditionBased = isConditionBased;
        }
    }

    /** Disallowed reasons. There could be multiple reasons if it is not allowed. */
    public enum DataDisallowedReason {
        // Soft failure reasons. A soft reason means that in certain conditions, data is still
        // allowed. Normally those reasons are due to users settings.
        /** Data is disabled by the user or policy. */
        DATA_DISABLED(false),
        /** Data roaming is disabled by the user. */
        ROAMING_DISABLED(false),
        /** Default data not selected. */
        DEFAULT_DATA_UNSELECTED(false),

        // Belows are all hard failure reasons. A hard reason means no matter what the data should
        // not be allowed.
        /** Data registration state is not in service. */
        NOT_IN_SERVICE(true),
        /** Data config is not ready. */
        DATA_CONFIG_NOT_READY(true),
        /** SIM is not ready. */
        SIM_NOT_READY(true),
        /** Concurrent voice and data is not allowed. */
        CONCURRENT_VOICE_DATA_NOT_ALLOWED(true),
        /** Service option not supported. */
        SERVICE_OPTION_NOT_SUPPORTED(true),
        /** Carrier notified data should be restricted. */
        DATA_RESTRICTED_BY_NETWORK(true),
        /** Radio power is off (i.e. airplane mode on) */
        RADIO_POWER_OFF(true),
        /** Data setup now allowed due to pending tear down all networks. */
        PENDING_TEAR_DOWN_ALL(true),
        /** Airplane mode is forcibly turned on by the carrier. */
        RADIO_DISABLED_BY_CARRIER(true),
        /** Underlying data service is not bound. */
        DATA_SERVICE_NOT_READY(true),
        /** Unable to find a suitable data profile. */
        NO_SUITABLE_DATA_PROFILE(true),
        /** Current data network type not allowed. */
        DATA_NETWORK_TYPE_NOT_ALLOWED(true),
        /** Device is currently in CDMA ECBM. */
        CDMA_EMERGENCY_CALLBACK_MODE(true),
        /** There is already a retry setup/handover scheduled. */
        RETRY_SCHEDULED(true),
        /** Network has explicitly request to throttle setup attempt. */
        DATA_THROTTLED(true),
        /** Data profile becomes invalid. (could be removed by the user, or SIM refresh, etc..) */
        DATA_PROFILE_INVALID(true),
        /** Data profile not preferred (i.e. users switch preferred profile in APN editor.) */
        DATA_PROFILE_NOT_PREFERRED(true),
        /** Handover is not allowed by policy. */
        NOT_ALLOWED_BY_POLICY(true),
        /** Data network is not in the right state. */
        ILLEGAL_STATE(true),
        /** VoPS is not supported by the network. */
        VOPS_NOT_SUPPORTED(true),
        /** Only one data network is allowed at one time. */
        ONLY_ALLOWED_SINGLE_NETWORK(true),
        /** Data enabled settings are not ready. */
        DATA_SETTINGS_NOT_READY(true),
        /** Handover max retry stopped but network is not on the preferred transport. */
        HANDOVER_RETRY_STOPPED(true);

        private final boolean mIsHardReason;

        /**
         * @return {@code true} if the disallowed reason is a hard reason.
         */
        public boolean isHardReason() {
            return mIsHardReason;
        }

        /**
         * Constructor
         *
         * @param isHardReason {@code true} if the disallowed reason is a hard reason. A hard reason
         * means no matter what the data should not be allowed. A soft reason means that in certain
         * conditions, data is still allowed.
         */
        DataDisallowedReason(boolean isHardReason) {
            mIsHardReason = isHardReason;
        }
    }

    /**
     * Data allowed reasons. There will be only one reason if data is allowed.
     */
    public enum DataAllowedReason {
        // Note that unlike disallowed reasons, we only have one allowed reason every time
        // when we check data is allowed or not. The order of these allowed reasons is very
        // important. The lower ones take precedence over the upper ones.
        /**
         * None. This is the initial value.
         */
        NONE,
        /**
         * The normal reason. This is the most common case.
         */
        NORMAL,
        /**
         * Data is allowed because an ongoing VoPS call depends on this network
         */
        IN_VOICE_CALL,
        /**
         * The network brought up by this network request is unmetered. Should allowed no matter
         * the user enables or disables data.
         */
        UNMETERED_USAGE,
        /**
         * The network request supports MMS and MMS is always allowed.
         */
        MMS_REQUEST,
        /**
         * The network request is restricted (i.e. Only privilege apps can access the network.)
         */
        RESTRICTED_REQUEST,
        /**
         * SUPL is allowed while emergency call is ongoing.
         */
        EMERGENCY_SUPL,
        /**
         * Data is allowed because the network request is for emergency. This should be always at
         * the bottom (i.e. highest priority)
         */
        EMERGENCY_REQUEST,
    }

    @Override
    public String toString() {
        StringBuilder evaluationStr = new StringBuilder();
        evaluationStr.append("Data evaluation: evaluation reason:" + mDataEvaluationReason + ", ");
        if (mDataDisallowedReasons.size() > 0) {
            evaluationStr.append("Data disallowed reasons:");
            for (DataDisallowedReason reason : mDataDisallowedReasons) {
                evaluationStr.append(" ").append(reason);
            }
        } else {
            evaluationStr.append("Data allowed reason:");
            evaluationStr.append(" ").append(mDataAllowedReason);
        }
        evaluationStr.append(", candidate profile=" + mCandidateDataProfile);
        evaluationStr.append(", time=" + DataUtils.systemTimeToString(mEvaluatedTime));
        return evaluationStr.toString();
    }

}