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