• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.server.connectivity;
18 
19 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
20 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
21 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
22 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
23 import static android.net.NetworkScore.KEEP_CONNECTED_NONE;
24 import static android.net.NetworkScore.POLICY_EXITING;
25 import static android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY;
26 import static android.net.NetworkScore.POLICY_YIELD_TO_BAD_WIFI;
27 
28 import android.annotation.IntDef;
29 import android.annotation.NonNull;
30 import android.net.NetworkAgentConfig;
31 import android.net.NetworkCapabilities;
32 import android.net.NetworkScore;
33 import android.net.NetworkScore.KeepConnectedReason;
34 
35 import com.android.internal.annotations.VisibleForTesting;
36 
37 import java.lang.annotation.Retention;
38 import java.lang.annotation.RetentionPolicy;
39 import java.util.StringJoiner;
40 
41 /**
42  * This class represents how desirable a network is.
43  *
44  * FullScore is very similar to NetworkScore, but it contains the bits that are managed
45  * by ConnectivityService. This provides static guarantee that all users must know whether
46  * they are handling a score that had the CS-managed bits set.
47  */
48 public class FullScore {
49     // This will be removed soon. Do *NOT* depend on it for any new code that is not part of
50     // a migration.
51     private final int mLegacyInt;
52 
53     /** @hide */
54     @Retention(RetentionPolicy.SOURCE)
55     @IntDef(prefix = {"POLICY_"}, value = {
56             POLICY_IS_VALIDATED,
57             POLICY_IS_VPN,
58             POLICY_EVER_USER_SELECTED,
59             POLICY_ACCEPT_UNVALIDATED,
60             POLICY_IS_UNMETERED
61     })
62     public @interface Policy {
63     }
64 
65     // Agent-managed policies are in NetworkScore. They start from 1.
66     // CS-managed policies, counting from 63 downward
67     // This network is validated. CS-managed because the source of truth is in NetworkCapabilities.
68     /** @hide */
69     public static final int POLICY_IS_VALIDATED = 63;
70 
71     // This is a VPN and behaves as one for scoring purposes.
72     /** @hide */
73     public static final int POLICY_IS_VPN = 62;
74 
75     // This network has been selected by the user manually from settings or a 3rd party app
76     // at least once. {@see NetworkAgentConfig#explicitlySelected}.
77     /** @hide */
78     public static final int POLICY_EVER_USER_SELECTED = 61;
79 
80     // The user has indicated in UI that this network should be used even if it doesn't
81     // validate. {@see NetworkAgentConfig#acceptUnvalidated}.
82     /** @hide */
83     public static final int POLICY_ACCEPT_UNVALIDATED = 60;
84 
85     // This network is unmetered. {@see NetworkCapabilities.NET_CAPABILITY_NOT_METERED}.
86     /** @hide */
87     public static final int POLICY_IS_UNMETERED = 59;
88 
89     // This network is invincible. This is useful for offers until there is an API to listen
90     // to requests.
91     /** @hide */
92     public static final int POLICY_IS_INVINCIBLE = 58;
93 
94     // This network has been validated at least once since it was connected, but not explicitly
95     // avoided in UI.
96     // TODO : remove setAvoidUnvalidated and instead disconnect the network when the user
97     // chooses to move away from this network, and remove this flag.
98     /** @hide */
99     public static final int POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD = 57;
100 
101     // To help iterate when printing
102     @VisibleForTesting
103     static final int MIN_CS_MANAGED_POLICY = POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD;
104     @VisibleForTesting
105     static final int MAX_CS_MANAGED_POLICY = POLICY_IS_VALIDATED;
106 
107     // Mask for policies in NetworkScore. This should have all bits managed by NetworkScore set
108     // and all bits managed by FullScore unset. As bits are handled from 0 up in NetworkScore and
109     // from 63 down in FullScore, cut at the 32nd bit for simplicity, but change this if some day
110     // there are more than 32 bits handled on either side.
111     // YIELD_TO_BAD_WIFI is temporarily handled by ConnectivityService.
112     private static final long EXTERNAL_POLICIES_MASK =
113             0x00000000FFFFFFFFL & ~(1L << POLICY_YIELD_TO_BAD_WIFI);
114 
115     @VisibleForTesting
policyNameOf(final int policy)116     static @NonNull String policyNameOf(final int policy) {
117         switch (policy) {
118             case POLICY_IS_VALIDATED: return "IS_VALIDATED";
119             case POLICY_IS_VPN: return "IS_VPN";
120             case POLICY_EVER_USER_SELECTED: return "EVER_USER_SELECTED";
121             case POLICY_ACCEPT_UNVALIDATED: return "ACCEPT_UNVALIDATED";
122             case POLICY_IS_UNMETERED: return "IS_UNMETERED";
123             case POLICY_YIELD_TO_BAD_WIFI: return "YIELD_TO_BAD_WIFI";
124             case POLICY_TRANSPORT_PRIMARY: return "TRANSPORT_PRIMARY";
125             case POLICY_EXITING: return "EXITING";
126             case POLICY_IS_INVINCIBLE: return "INVINCIBLE";
127             case POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD: return "EVER_VALIDATED";
128         }
129         throw new IllegalArgumentException("Unknown policy : " + policy);
130     }
131 
132     // Bitmask of all the policies applied to this score.
133     private final long mPolicies;
134 
135     private final int mKeepConnectedReason;
136 
FullScore(final int legacyInt, final long policies, @KeepConnectedReason final int keepConnectedReason)137     FullScore(final int legacyInt, final long policies,
138             @KeepConnectedReason final int keepConnectedReason) {
139         mLegacyInt = legacyInt;
140         mPolicies = policies;
141         mKeepConnectedReason = keepConnectedReason;
142     }
143 
144     /**
145      * Given a score supplied by the NetworkAgent and CS-managed objects, produce a full score.
146      *
147      * @param score the score supplied by the agent
148      * @param caps the NetworkCapabilities of the network
149      * @param config the NetworkAgentConfig of the network
150      * @param everValidated whether this network has ever validated
151      * @param yieldToBadWiFi whether this network yields to a previously validated wifi gone bad
152      * @return a FullScore that is appropriate to use for ranking.
153      */
154     // TODO : this shouldn't manage bad wifi avoidance – instead this should be done by the
155     // telephony factory, so that it depends on the carrier. For now this is handled by
156     // connectivity for backward compatibility.
fromNetworkScore(@onNull final NetworkScore score, @NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config, final boolean everValidated, final boolean yieldToBadWiFi)157     public static FullScore fromNetworkScore(@NonNull final NetworkScore score,
158             @NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config,
159             final boolean everValidated, final boolean yieldToBadWiFi) {
160         return withPolicies(score.getLegacyInt(), score.getPolicies(),
161                 score.getKeepConnectedReason(),
162                 caps.hasCapability(NET_CAPABILITY_VALIDATED),
163                 caps.hasTransport(TRANSPORT_VPN),
164                 caps.hasCapability(NET_CAPABILITY_NOT_METERED),
165                 everValidated,
166                 config.explicitlySelected,
167                 config.acceptUnvalidated,
168                 yieldToBadWiFi,
169                 false /* invincible */); // only prospective scores can be invincible
170     }
171 
172     /**
173      * Given a score supplied by a NetworkProvider, produce a prospective score for an offer.
174      *
175      * NetworkOffers have score filters that are compared to the scores of actual networks
176      * to see if they could possibly beat the current satisfier. Some things the agent can't
177      * know in advance ; a good example is the validation bit – some networks will validate,
178      * others won't. For comparison purposes, assume the best, so all possibly beneficial
179      * networks will be brought up.
180      *
181      * @param score the score supplied by the agent for this offer
182      * @param caps the capabilities supplied by the agent for this offer
183      * @return a FullScore appropriate for comparing to actual network's scores.
184      */
makeProspectiveScore(@onNull final NetworkScore score, @NonNull final NetworkCapabilities caps)185     public static FullScore makeProspectiveScore(@NonNull final NetworkScore score,
186             @NonNull final NetworkCapabilities caps) {
187         // If the network offers Internet access, it may validate.
188         final boolean mayValidate = caps.hasCapability(NET_CAPABILITY_INTERNET);
189         // VPN transports are known in advance.
190         final boolean vpn = caps.hasTransport(TRANSPORT_VPN);
191         // Prospective scores are always unmetered, because unmetered networks are stronger
192         // than metered networks, and it's not known in advance whether the network is metered.
193         final boolean unmetered = true;
194         // If the offer may validate, then it should be considered to have validated at some point
195         final boolean everValidated = mayValidate;
196         // The network hasn't been chosen by the user (yet, at least).
197         final boolean everUserSelected = false;
198         // Don't assume the user will accept unvalidated connectivity.
199         final boolean acceptUnvalidated = false;
200         // Don't assume clinging to bad wifi
201         final boolean yieldToBadWiFi = false;
202         // A prospective score is invincible if the legacy int in the filter is over the maximum
203         // score.
204         final boolean invincible = score.getLegacyInt() > NetworkRanker.LEGACY_INT_MAX;
205         return withPolicies(score.getLegacyInt(), score.getPolicies(), KEEP_CONNECTED_NONE,
206                 mayValidate, vpn, unmetered, everValidated, everUserSelected, acceptUnvalidated,
207                 yieldToBadWiFi, invincible);
208     }
209 
210     /**
211      * Return a new score given updated caps and config.
212      *
213      * @param caps the NetworkCapabilities of the network
214      * @param config the NetworkAgentConfig of the network
215      * @return a score with the policies from the arguments reset
216      */
217     // TODO : this shouldn't manage bad wifi avoidance – instead this should be done by the
218     // telephony factory, so that it depends on the carrier. For now this is handled by
219     // connectivity for backward compatibility.
mixInScore(@onNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config, final boolean everValidated, final boolean yieldToBadWifi)220     public FullScore mixInScore(@NonNull final NetworkCapabilities caps,
221             @NonNull final NetworkAgentConfig config,
222             final boolean everValidated,
223             final boolean yieldToBadWifi) {
224         return withPolicies(mLegacyInt, mPolicies, mKeepConnectedReason,
225                 caps.hasCapability(NET_CAPABILITY_VALIDATED),
226                 caps.hasTransport(TRANSPORT_VPN),
227                 caps.hasCapability(NET_CAPABILITY_NOT_METERED),
228                 everValidated,
229                 config.explicitlySelected,
230                 config.acceptUnvalidated,
231                 yieldToBadWifi,
232                 false /* invincible */); // only prospective scores can be invincible
233     }
234 
235     // TODO : this shouldn't manage bad wifi avoidance – instead this should be done by the
236     // telephony factory, so that it depends on the carrier. For now this is handled by
237     // connectivity for backward compatibility.
withPolicies(@onNull final int legacyInt, final long externalPolicies, @KeepConnectedReason final int keepConnectedReason, final boolean isValidated, final boolean isVpn, final boolean isUnmetered, final boolean everValidated, final boolean everUserSelected, final boolean acceptUnvalidated, final boolean yieldToBadWiFi, final boolean invincible)238     private static FullScore withPolicies(@NonNull final int legacyInt,
239             final long externalPolicies,
240             @KeepConnectedReason final int keepConnectedReason,
241             final boolean isValidated,
242             final boolean isVpn,
243             final boolean isUnmetered,
244             final boolean everValidated,
245             final boolean everUserSelected,
246             final boolean acceptUnvalidated,
247             final boolean yieldToBadWiFi,
248             final boolean invincible) {
249         return new FullScore(legacyInt, (externalPolicies & EXTERNAL_POLICIES_MASK)
250                 | (isValidated       ? 1L << POLICY_IS_VALIDATED : 0)
251                 | (isVpn             ? 1L << POLICY_IS_VPN : 0)
252                 | (isUnmetered       ? 1L << POLICY_IS_UNMETERED : 0)
253                 | (everValidated     ? 1L << POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD : 0)
254                 | (everUserSelected  ? 1L << POLICY_EVER_USER_SELECTED : 0)
255                 | (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0)
256                 | (yieldToBadWiFi    ? 1L << POLICY_YIELD_TO_BAD_WIFI : 0)
257                 | (invincible        ? 1L << POLICY_IS_INVINCIBLE : 0),
258                 keepConnectedReason);
259     }
260 
261     /**
262      * Returns this score but validated.
263      */
asValidated()264     public FullScore asValidated() {
265         return new FullScore(mLegacyInt, mPolicies | (1L << POLICY_IS_VALIDATED),
266                 mKeepConnectedReason);
267     }
268 
269     /**
270      * For backward compatibility, get the legacy int.
271      * This will be removed before S is published.
272      */
getLegacyInt()273     public int getLegacyInt() {
274         return getLegacyInt(false /* pretendValidated */);
275     }
276 
getLegacyIntAsValidated()277     public int getLegacyIntAsValidated() {
278         return getLegacyInt(true /* pretendValidated */);
279     }
280 
281     // TODO : remove these two constants
282     // Penalty applied to scores of Networks that have not been validated.
283     private static final int UNVALIDATED_SCORE_PENALTY = 40;
284 
285     // Score for a network that can be used unvalidated
286     private static final int ACCEPT_UNVALIDATED_NETWORK_SCORE = 100;
287 
getLegacyInt(boolean pretendValidated)288     private int getLegacyInt(boolean pretendValidated) {
289         // If the user has chosen this network at least once, give it the maximum score when
290         // checking to pretend it's validated, or if it doesn't need to validate because the
291         // user said to use it even if it doesn't validate.
292         // This ensures that networks that have been selected in UI are not torn down before the
293         // user gets a chance to prefer it when a higher-scoring network (e.g., Ethernet) is
294         // available.
295         if (hasPolicy(POLICY_EVER_USER_SELECTED)
296                 && (hasPolicy(POLICY_ACCEPT_UNVALIDATED) || pretendValidated)) {
297             return ACCEPT_UNVALIDATED_NETWORK_SCORE;
298         }
299 
300         int score = mLegacyInt;
301         // Except for VPNs, networks are subject to a penalty for not being validated.
302         // Apply the penalty unless the network is a VPN, or it's validated or pretending to be.
303         if (!hasPolicy(POLICY_IS_VALIDATED) && !pretendValidated && !hasPolicy(POLICY_IS_VPN)) {
304             score -= UNVALIDATED_SCORE_PENALTY;
305         }
306         if (score < 0) score = 0;
307         return score;
308     }
309 
310     /**
311      * @return whether this score has a particular policy.
312      */
313     @VisibleForTesting
hasPolicy(final int policy)314     public boolean hasPolicy(final int policy) {
315         return 0 != (mPolicies & (1L << policy));
316     }
317 
318     /**
319      * Returns the keep-connected reason, or KEEP_CONNECTED_NONE.
320      */
getKeepConnectedReason()321     public int getKeepConnectedReason() {
322         return mKeepConnectedReason;
323     }
324 
325     // Example output :
326     // Score(50 ; Policies : EVER_USER_SELECTED&IS_VALIDATED)
327     @Override
toString()328     public String toString() {
329         final StringJoiner sj = new StringJoiner(
330                 "&", // delimiter
331                 "Score(" + mLegacyInt + " ; KeepConnected : " + mKeepConnectedReason
332                         + " ; Policies : ", // prefix
333                 ")"); // suffix
334         for (int i = NetworkScore.MIN_AGENT_MANAGED_POLICY;
335                 i <= NetworkScore.MAX_AGENT_MANAGED_POLICY; ++i) {
336             if (hasPolicy(i)) sj.add(policyNameOf(i));
337         }
338         for (int i = MIN_CS_MANAGED_POLICY; i <= MAX_CS_MANAGED_POLICY; ++i) {
339             if (hasPolicy(i)) sj.add(policyNameOf(i));
340         }
341         return sj.toString();
342     }
343 }
344