1 /* 2 * Copyright (C) 2014 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 android.net; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemApi; 22 import android.os.Bundle; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 26 import java.util.Objects; 27 import java.util.Set; 28 29 /** 30 * A network identifier along with a score for the quality of that network. 31 * 32 * @deprecated as part of the {@link NetworkScoreManager} deprecation. 33 * @hide 34 */ 35 @Deprecated 36 @SystemApi 37 public class ScoredNetwork implements Parcelable { 38 39 /** 40 * Key used with the {@link #attributes} bundle to define the badging curve. 41 * 42 * <p>The badging curve is a {@link RssiCurve} used to map different RSSI values to {@link 43 * NetworkBadging.Badging} enums. 44 */ 45 public static final String ATTRIBUTES_KEY_BADGING_CURVE = 46 "android.net.attributes.key.BADGING_CURVE"; 47 /** 48 * Extra used with {@link #attributes} to specify whether the 49 * network is believed to have a captive portal. 50 * <p> 51 * This data may be used, for example, to display a visual indicator 52 * in a network selection list. 53 * <p> 54 * Note that the this extra conveys the possible presence of a 55 * captive portal, not its state or the user's ability to open 56 * the portal. 57 * <p> 58 * If no value is associated with this key then it's unknown. 59 */ 60 public static final String ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL = 61 "android.net.attributes.key.HAS_CAPTIVE_PORTAL"; 62 63 /** 64 * Key used with the {@link #attributes} bundle to define the rankingScoreOffset int value. 65 * 66 * <p>The rankingScoreOffset is used when calculating the ranking score used to rank networks 67 * against one another. See {@link #calculateRankingScore} for more information. 68 */ 69 public static final String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET = 70 "android.net.attributes.key.RANKING_SCORE_OFFSET"; 71 72 /** A {@link NetworkKey} uniquely identifying this network. */ 73 public final NetworkKey networkKey; 74 75 /** 76 * The {@link RssiCurve} representing the scores for this network based on the RSSI. 77 * 78 * <p>This field is optional and may be set to null to indicate that no score is available for 79 * this network at this time. Such networks, along with networks for which the scorer has not 80 * responded, are always prioritized below scored networks, regardless of the score. 81 */ 82 public final RssiCurve rssiCurve; 83 84 /** 85 * A boolean value that indicates whether or not the network is believed to be metered. 86 * 87 * <p>A network can be classified as metered if the user would be 88 * sensitive to heavy data usage on that connection due to monetary costs, 89 * data limitations or battery/performance issues. A typical example would 90 * be a wifi connection where the user would be charged for usage. 91 */ 92 public final boolean meteredHint; 93 94 /** 95 * An additional collection of optional attributes set by 96 * the Network Recommendation Provider. 97 * 98 * @see #ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL 99 * @see #ATTRIBUTES_KEY_RANKING_SCORE_OFFSET 100 */ 101 @Nullable 102 public final Bundle attributes; 103 104 /** 105 * Construct a new {@link ScoredNetwork}. 106 * 107 * @param networkKey the {@link NetworkKey} uniquely identifying this network. 108 * @param rssiCurve the {@link RssiCurve} representing the scores for this network based on the 109 * RSSI. This field is optional, and may be skipped to represent a network which the scorer 110 * has opted not to score at this time. Passing a null value here is strongly preferred to 111 * not returning any {@link ScoredNetwork} for a given {@link NetworkKey} because it 112 * indicates to the system not to request scores for this network in the future, although 113 * the scorer may choose to issue an out-of-band update at any time. 114 */ ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve)115 public ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve) { 116 this(networkKey, rssiCurve, false /* meteredHint */); 117 } 118 119 /** 120 * Construct a new {@link ScoredNetwork}. 121 * 122 * @param networkKey the {@link NetworkKey} uniquely identifying this network. 123 * @param rssiCurve the {@link RssiCurve} representing the scores for this network based on the 124 * RSSI. This field is optional, and may be skipped to represent a network which the scorer 125 * has opted not to score at this time. Passing a null value here is strongly preferred to 126 * not returning any {@link ScoredNetwork} for a given {@link NetworkKey} because it 127 * indicates to the system not to request scores for this network in the future, although 128 * the scorer may choose to issue an out-of-band update at any time. 129 * @param meteredHint A boolean value indicating whether or not the network is believed to be 130 * metered. 131 */ ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve, boolean meteredHint)132 public ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve, boolean meteredHint) { 133 this(networkKey, rssiCurve, meteredHint, null /* attributes */); 134 } 135 136 /** 137 * Construct a new {@link ScoredNetwork}. 138 * 139 * @param networkKey the {@link NetworkKey} uniquely identifying this network 140 * @param rssiCurve the {@link RssiCurve} representing the scores for this network based on the 141 * RSSI. This field is optional, and may be skipped to represent a network which the scorer 142 * has opted not to score at this time. Passing a null value here is strongly preferred to 143 * not returning any {@link ScoredNetwork} for a given {@link NetworkKey} because it 144 * indicates to the system not to request scores for this network in the future, although 145 * the scorer may choose to issue an out-of-band update at any time. 146 * @param meteredHint a boolean value indicating whether or not the network is believed to be 147 * metered 148 * @param attributes optional provider specific attributes 149 */ ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve, boolean meteredHint, @Nullable Bundle attributes)150 public ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve, boolean meteredHint, 151 @Nullable Bundle attributes) { 152 this.networkKey = networkKey; 153 this.rssiCurve = rssiCurve; 154 this.meteredHint = meteredHint; 155 this.attributes = attributes; 156 } 157 ScoredNetwork(Parcel in)158 private ScoredNetwork(Parcel in) { 159 networkKey = NetworkKey.CREATOR.createFromParcel(in); 160 if (in.readByte() == 1) { 161 rssiCurve = RssiCurve.CREATOR.createFromParcel(in); 162 } else { 163 rssiCurve = null; 164 } 165 meteredHint = (in.readByte() == 1); 166 attributes = in.readBundle(); 167 } 168 169 @Override describeContents()170 public int describeContents() { 171 return 0; 172 } 173 174 @Override writeToParcel(Parcel out, int flags)175 public void writeToParcel(Parcel out, int flags) { 176 networkKey.writeToParcel(out, flags); 177 if (rssiCurve != null) { 178 out.writeByte((byte) 1); 179 rssiCurve.writeToParcel(out, flags); 180 } else { 181 out.writeByte((byte) 0); 182 } 183 out.writeByte((byte) (meteredHint ? 1 : 0)); 184 out.writeBundle(attributes); 185 } 186 187 @Override equals(@ullable Object o)188 public boolean equals(@Nullable Object o) { 189 if (this == o) return true; 190 if (o == null || getClass() != o.getClass()) return false; 191 192 ScoredNetwork that = (ScoredNetwork) o; 193 194 return Objects.equals(networkKey, that.networkKey) 195 && Objects.equals(rssiCurve, that.rssiCurve) 196 && Objects.equals(meteredHint, that.meteredHint) 197 && bundleEquals(attributes, that.attributes); 198 } 199 bundleEquals(Bundle bundle1, Bundle bundle2)200 private boolean bundleEquals(Bundle bundle1, Bundle bundle2) { 201 if (bundle1 == bundle2) { 202 return true; 203 } 204 if (bundle1 == null || bundle2 == null) { 205 return false; 206 } 207 if (bundle1.size() != bundle2.size()) { 208 return false; 209 } 210 Set<String> keys = bundle1.keySet(); 211 for (String key : keys) { 212 Object value1 = bundle1.get(key); 213 Object value2 = bundle2.get(key); 214 if (!Objects.equals(value1, value2)) { 215 return false; 216 } 217 } 218 return true; 219 } 220 221 @Override hashCode()222 public int hashCode() { 223 return Objects.hash(networkKey, rssiCurve, meteredHint, attributes); 224 } 225 226 @NonNull 227 @Override toString()228 public String toString() { 229 StringBuilder out = new StringBuilder( 230 "ScoredNetwork{" + 231 "networkKey=" + networkKey + 232 ", rssiCurve=" + rssiCurve + 233 ", meteredHint=" + meteredHint); 234 // calling isEmpty will unparcel the bundle so its contents can be converted to a string 235 if (attributes != null && !attributes.isEmpty()) { 236 out.append(", attributes=" + attributes); 237 } 238 out.append('}'); 239 return out.toString(); 240 } 241 242 /** 243 * Returns true if a ranking score can be calculated for this network. 244 * 245 * @hide 246 */ hasRankingScore()247 public boolean hasRankingScore() { 248 return (rssiCurve != null) 249 || (attributes != null 250 && attributes.containsKey(ATTRIBUTES_KEY_RANKING_SCORE_OFFSET)); 251 } 252 253 /** 254 * Returns a ranking score for a given RSSI which can be used to comparatively 255 * rank networks. 256 * 257 * <p>The score obtained by the rssiCurve is bitshifted left by 8 bits to expand it to an 258 * integer and then the offset is added. If the addition operation overflows or underflows, 259 * Integer.MAX_VALUE and Integer.MIN_VALUE will be returned respectively. 260 * 261 * <p>{@link #hasRankingScore} should be called first to ensure this network is capable 262 * of returning a ranking score. 263 * 264 * @throws UnsupportedOperationException if there is no RssiCurve and no rankingScoreOffset 265 * for this network (hasRankingScore returns false). 266 * 267 * @hide 268 */ calculateRankingScore(int rssi)269 public int calculateRankingScore(int rssi) throws UnsupportedOperationException { 270 if (!hasRankingScore()) { 271 throw new UnsupportedOperationException( 272 "Either rssiCurve or rankingScoreOffset is required to calculate the " 273 + "ranking score"); 274 } 275 276 int offset = 0; 277 if (attributes != null) { 278 offset += attributes.getInt(ATTRIBUTES_KEY_RANKING_SCORE_OFFSET, 0 /* default */); 279 } 280 281 int score = (rssiCurve == null) ? 0 : rssiCurve.lookupScore(rssi) << Byte.SIZE; 282 283 try { 284 return Math.addExact(score, offset); 285 } catch (ArithmeticException e) { 286 return (score < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE; 287 } 288 } 289 290 /** 291 * Return the {@link NetworkBadging.Badging} enum for this network for the given RSSI, derived from the 292 * badging curve. 293 * 294 * <p>If no badging curve is present, {@link #BADGE_NONE} will be returned. 295 * 296 * @param rssi The rssi level for which the badge should be calculated 297 */ 298 @NetworkBadging.Badging calculateBadge(int rssi)299 public int calculateBadge(int rssi) { 300 if (attributes != null && attributes.containsKey(ATTRIBUTES_KEY_BADGING_CURVE)) { 301 RssiCurve badgingCurve = 302 attributes.getParcelable(ATTRIBUTES_KEY_BADGING_CURVE, android.net.RssiCurve.class); 303 return badgingCurve.lookupScore(rssi); 304 } 305 306 return NetworkBadging.BADGING_NONE; 307 } 308 309 public static final @android.annotation.NonNull Parcelable.Creator<ScoredNetwork> CREATOR = 310 new Parcelable.Creator<ScoredNetwork>() { 311 @Override 312 public ScoredNetwork createFromParcel(Parcel in) { 313 return new ScoredNetwork(in); 314 } 315 316 @Override 317 public ScoredNetwork[] newArray(int size) { 318 return new ScoredNetwork[size]; 319 } 320 }; 321 } 322