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